├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── styles.xml
│ │ │ │ └── colors.xml
│ │ │ ├── drawable
│ │ │ │ ├── arrow_up.png
│ │ │ │ └── arrow_down.png
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ └── layout
│ │ │ │ ├── activity_main.xml
│ │ │ │ ├── parent_layout.xml
│ │ │ │ └── child_layout.xml
│ │ ├── java
│ │ │ └── me
│ │ │ │ └── weishu
│ │ │ │ └── epic
│ │ │ │ └── samples
│ │ │ │ ├── tests
│ │ │ │ ├── invoketype
│ │ │ │ │ ├── InvokeTypeTarget.java
│ │ │ │ │ └── InvokeConstructor.java
│ │ │ │ ├── custom
│ │ │ │ │ ├── Case.java
│ │ │ │ │ ├── Case15_StopJit.java
│ │ │ │ │ ├── Case14_GC.java
│ │ │ │ │ ├── Case13_FastNative.java
│ │ │ │ │ ├── Case7.java
│ │ │ │ │ ├── CaseManager.java
│ │ │ │ │ ├── Case10_Default_Constructor.java
│ │ │ │ │ ├── Case3.java
│ │ │ │ │ ├── Case8_Activity_onCreate.java
│ │ │ │ │ ├── Case18_returnConst.java
│ │ │ │ │ ├── Case4.java
│ │ │ │ │ ├── Case11_SuspendAll.java
│ │ │ │ │ ├── Case2.java
│ │ │ │ │ ├── Case17_SameMethod.java
│ │ │ │ │ ├── Case5.java
│ │ │ │ │ ├── Case6.java
│ │ │ │ │ ├── Case1.java
│ │ │ │ │ ├── Target.java
│ │ │ │ │ ├── Case12_MultiCallback.java
│ │ │ │ │ ├── Case16_SameEntry.java
│ │ │ │ │ └── Case9_ThreadMonitor.java
│ │ │ │ ├── arguments
│ │ │ │ │ ├── ArgStatic8.java
│ │ │ │ │ ├── ArgStatic4.java
│ │ │ │ │ ├── ArgStatic888.java
│ │ │ │ │ ├── ArgStatic488.java
│ │ │ │ │ ├── ArgStatic848.java
│ │ │ │ │ ├── ArgStatic884.java
│ │ │ │ │ ├── ArgStatic48.java
│ │ │ │ │ ├── ArgStatic84.java
│ │ │ │ │ ├── ArgStatic8888.java
│ │ │ │ │ ├── ArgStatic44.java
│ │ │ │ │ ├── ArgStatic484.java
│ │ │ │ │ ├── ArgStatic4888.java
│ │ │ │ │ ├── ArgStatic844.java
│ │ │ │ │ ├── ArgStatic8488.java
│ │ │ │ │ ├── ArgStatic8848.java
│ │ │ │ │ ├── ArgStatic8884.java
│ │ │ │ │ ├── ArgStatic448.java
│ │ │ │ │ ├── ArgStatic4488.java
│ │ │ │ │ ├── ArgStatic4848.java
│ │ │ │ │ ├── ArgStatic4884.java
│ │ │ │ │ ├── ArgStatic8448.java
│ │ │ │ │ ├── ArgStatic8484.java
│ │ │ │ │ ├── ArgStatic8844.java
│ │ │ │ │ ├── ArgStatic8444.java
│ │ │ │ │ ├── ArgStatic4444.java
│ │ │ │ │ ├── ArgStatic4448.java
│ │ │ │ │ ├── ArgStatic4484.java
│ │ │ │ │ ├── ArgStatic4844.java
│ │ │ │ │ ├── ArgStatic88.java
│ │ │ │ │ ├── ArgStatic444.java
│ │ │ │ │ ├── ArgStatic0.java
│ │ │ │ │ └── AbsArgStaticCase.java
│ │ │ │ ├── TestSuite.java
│ │ │ │ ├── CallingConventationTest.java
│ │ │ │ ├── TestCase.java
│ │ │ │ ├── returntype
│ │ │ │ │ ├── LongType.java
│ │ │ │ │ ├── CharType.java
│ │ │ │ │ ├── FloatType.java
│ │ │ │ │ ├── ShortType.java
│ │ │ │ │ ├── BooleanType.java
│ │ │ │ │ ├── DoubleType.java
│ │ │ │ │ ├── StringType.java
│ │ │ │ │ ├── IntType.java
│ │ │ │ │ ├── ByteType.java
│ │ │ │ │ ├── CustomType.java
│ │ │ │ │ ├── StringArrayType.java
│ │ │ │ │ ├── VoidType.java
│ │ │ │ │ └── ReturnTypeTarget.java
│ │ │ │ ├── LogMethodHook.java
│ │ │ │ └── TestManager.java
│ │ │ │ ├── MainApplication.java
│ │ │ │ └── MainActivity.java
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── me
│ │ │ └── weishu
│ │ │ └── epic
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── me
│ │ └── weishu
│ │ └── epic
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── library
├── .gitignore
├── src
│ └── main
│ │ ├── cpp
│ │ ├── .gitignore
│ │ ├── build_with_cmake
│ │ ├── fake_dlfcn.h
│ │ ├── CMakeLists.txt
│ │ ├── art.cpp
│ │ └── art.h
│ │ ├── res
│ │ └── values
│ │ │ └── strings.xml
│ │ ├── jniLibs
│ │ └── armeabi
│ │ │ └── libdexposed.so
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ ├── com
│ │ └── taobao
│ │ │ └── android
│ │ │ └── dexposed
│ │ │ ├── utility
│ │ │ ├── NeverCalled.java
│ │ │ ├── Logger.java
│ │ │ ├── Runtime.java
│ │ │ ├── NougatPolicy.java
│ │ │ ├── Platform.java
│ │ │ ├── Debug.java
│ │ │ └── Unsafe.java
│ │ │ └── DeviceCheck.java
│ │ └── me
│ │ └── weishu
│ │ └── epic
│ │ └── art
│ │ ├── arch
│ │ ├── ShellCode.java
│ │ ├── Arm64.java
│ │ ├── Arm64_2.java
│ │ └── Thumb2.java
│ │ ├── EpicNative.java
│ │ ├── Trampoline.java
│ │ └── Epic.java
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── .idea
├── encodings.xml
├── misc.xml
├── runConfigurations.xml
└── gradle.xml
├── .github
├── workflows
│ └── android.yml
└── FUNDING.yml
├── gradle.properties
├── gradlew.bat
├── README_cn.md
├── README.md
└── gradlew
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/library/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/library/src/main/cpp/.gitignore:
--------------------------------------------------------------------------------
1 | build/
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':library'
2 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tiann/epic/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | epic
3 |
4 |
--------------------------------------------------------------------------------
/library/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | epic
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/arrow_up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tiann/epic/HEAD/app/src/main/res/drawable/arrow_up.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/arrow_down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tiann/epic/HEAD/app/src/main/res/drawable/arrow_down.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tiann/epic/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tiann/epic/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tiann/epic/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tiann/epic/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tiann/epic/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/library/src/main/jniLibs/armeabi/libdexposed.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tiann/epic/HEAD/library/src/main/jniLibs/armeabi/libdexposed.so
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tiann/epic/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tiann/epic/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tiann/epic/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tiann/epic/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tiann/epic/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 | .idea
11 |
--------------------------------------------------------------------------------
/library/src/main/cpp/build_with_cmake:
--------------------------------------------------------------------------------
1 | #! /bin/sh
2 | cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_SDK/ndk-bundle/build/cmake/android.toolchain.cmake -DANDROID_TOOLCHAIN=gcc -H. -Bbuild && cd build && make
3 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/invoketype/InvokeTypeTarget.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.invoketype;
2 |
3 | /**
4 | * Created by weishu on 17/11/14.
5 | */
6 |
7 | public class InvokeTypeTarget {
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/custom/Case.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.custom;
2 |
3 | /**
4 | * Created by weishu on 17/11/6.
5 | */
6 |
7 | public interface Case {
8 | void hook();
9 |
10 | boolean validate(Object... args);
11 | }
12 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Nov 07 13:44:26 CST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
7 |
--------------------------------------------------------------------------------
/.github/workflows/android.yml:
--------------------------------------------------------------------------------
1 | name: Android CI
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 |
8 | runs-on: ubuntu-latest
9 |
10 | steps:
11 | - uses: actions/checkout@v1
12 | - name: set up JDK 1.8
13 | uses: actions/setup-java@v1
14 | with:
15 | java-version: 1.8
16 | - name: Build with Gradle
17 | run: ./gradlew build
18 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic8.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.arguments;
2 |
3 | /**
4 | * @author weishu
5 | * @date 17/11/14.
6 | */
7 |
8 | public class ArgStatic8 extends AbsArgStaticCase {
9 |
10 | @Override
11 | protected void makeCall(long... args) {
12 | ArgumentTarget.arg1(args[0]);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic4.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.arguments;
2 |
3 | /**
4 | * @author weishu
5 | * @date 17/11/14.
6 | */
7 |
8 | public class ArgStatic4 extends AbsArgStaticCase {
9 |
10 | @Override
11 | protected void makeCall(long... args) {
12 | ArgumentTarget.arg1((int)args[0]);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic888.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.arguments;
2 |
3 |
4 | /**
5 | * Created by weishu on 17/11/14.
6 | */
7 | public class ArgStatic888 extends AbsArgStaticCase {
8 | @Override
9 | protected void makeCall(long... args) {
10 | ArgumentTarget.arg3(args[0], args[1], args[2]);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic488.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.arguments;
2 |
3 | /**
4 | * Created by weishu on 17/11/14.
5 | */
6 |
7 | public class ArgStatic488 extends AbsArgStaticCase {
8 | @Override
9 | protected void makeCall(long... args) {
10 | ArgumentTarget.arg3((int) args[0], args[1], args[2]);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic848.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.arguments;
2 |
3 | /**
4 | * Created by weishu on 17/11/14.
5 | */
6 |
7 | public class ArgStatic848 extends AbsArgStaticCase {
8 | @Override
9 | protected void makeCall(long... args) {
10 | ArgumentTarget.arg3(args[0], (int) args[1], args[2]);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic884.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.arguments;
2 |
3 |
4 | /**
5 | * Created by weishu on 17/11/14.
6 | */
7 | public class ArgStatic884 extends AbsArgStaticCase {
8 | @Override
9 | protected void makeCall(long... args) {
10 | ArgumentTarget.arg3(args[0], args[1], (int) args[2]);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic48.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.arguments;
2 |
3 | /**
4 | * @author weishu
5 | * @date 17/11/14.
6 | */
7 |
8 | public class ArgStatic48 extends AbsArgStaticCase {
9 |
10 | @Override
11 | protected void makeCall(long... args) {
12 | ArgumentTarget.arg2((int) args[0], args[1]);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic84.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.arguments;
2 |
3 | /**
4 | * @author weishu
5 | * @date 17/11/14.
6 | */
7 |
8 | public class ArgStatic84 extends AbsArgStaticCase {
9 |
10 | @Override
11 | protected void makeCall(long... args) {
12 | ArgumentTarget.arg2(args[0], (int)args[1]);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic8888.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.arguments;
2 |
3 |
4 | /**
5 | * Created by weishu on 17/11/14.
6 | */
7 | public class ArgStatic8888 extends AbsArgStaticCase {
8 | @Override
9 | protected void makeCall(long... args) {
10 | ArgumentTarget.arg4(args[0], args[1], args[2], args[3]);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic44.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.arguments;
2 |
3 | /**
4 | * @author weishu
5 | * @date 17/11/14.
6 | */
7 |
8 | public class ArgStatic44 extends AbsArgStaticCase {
9 |
10 | @Override
11 | protected void makeCall(long... args) {
12 | ArgumentTarget.arg2((int) args[0], (int) args[1]);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic484.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.arguments;
2 |
3 | /**
4 | * Created by weishu on 17/11/14.
5 | */
6 |
7 | public class ArgStatic484 extends AbsArgStaticCase {
8 | @Override
9 | protected void makeCall(long... args) {
10 | ArgumentTarget.arg3((int) args[0], args[1], (int) args[2]);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic4888.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.arguments;
2 |
3 |
4 | /**
5 | * Created by weishu on 17/11/14.
6 | */
7 | public class ArgStatic4888 extends AbsArgStaticCase {
8 | @Override
9 | protected void makeCall(long... args) {
10 | ArgumentTarget.arg4((int)args[0], args[1], args[2], args[3]);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic844.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.arguments;
2 |
3 | /**
4 | * Created by weishu on 17/11/14.
5 | */
6 |
7 | public class ArgStatic844 extends AbsArgStaticCase {
8 | @Override
9 | protected void makeCall(long... args) {
10 | ArgumentTarget.arg3(args[0], (int) args[1], (int) args[2]);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic8488.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.arguments;
2 |
3 |
4 | /**
5 | * Created by weishu on 17/11/14.
6 | */
7 | public class ArgStatic8488 extends AbsArgStaticCase {
8 | @Override
9 | protected void makeCall(long... args) {
10 | ArgumentTarget.arg4(args[0], (int)args[1], args[2], args[3]);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic8848.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.arguments;
2 |
3 |
4 | /**
5 | * Created by weishu on 17/11/14.
6 | */
7 | public class ArgStatic8848 extends AbsArgStaticCase {
8 | @Override
9 | protected void makeCall(long... args) {
10 | ArgumentTarget.arg4(args[0], args[1], (int)args[2], args[3]);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic8884.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.arguments;
2 |
3 |
4 | /**
5 | * Created by weishu on 17/11/14.
6 | */
7 | public class ArgStatic8884 extends AbsArgStaticCase {
8 | @Override
9 | protected void makeCall(long... args) {
10 | ArgumentTarget.arg4(args[0], args[1], args[2], (int)args[3]);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic448.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.arguments;
2 |
3 | /**
4 | * Created by weishu on 17/11/14.
5 | */
6 |
7 | public class ArgStatic448 extends AbsArgStaticCase {
8 |
9 | @Override
10 | protected void makeCall(long... args) {
11 | ArgumentTarget.arg3((int) args[0], (int) args[1], args[2]);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic4488.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.arguments;
2 |
3 |
4 | /**
5 | * Created by weishu on 17/11/14.
6 | */
7 | public class ArgStatic4488 extends AbsArgStaticCase {
8 | @Override
9 | protected void makeCall(long... args) {
10 | ArgumentTarget.arg4((int)args[0], (int)args[1], args[2], args[3]);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic4848.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.arguments;
2 |
3 |
4 | /**
5 | * Created by weishu on 17/11/14.
6 | */
7 | public class ArgStatic4848 extends AbsArgStaticCase {
8 | @Override
9 | protected void makeCall(long... args) {
10 | ArgumentTarget.arg4((int)args[0], args[1], (int)args[2], args[3]);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic4884.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.arguments;
2 |
3 |
4 | /**
5 | * Created by weishu on 17/11/14.
6 | */
7 | public class ArgStatic4884 extends AbsArgStaticCase {
8 | @Override
9 | protected void makeCall(long... args) {
10 | ArgumentTarget.arg4((int)args[0], args[1], args[2], (int)args[3]);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic8448.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.arguments;
2 |
3 |
4 | /**
5 | * Created by weishu on 17/11/14.
6 | */
7 | public class ArgStatic8448 extends AbsArgStaticCase {
8 | @Override
9 | protected void makeCall(long... args) {
10 | ArgumentTarget.arg4(args[0], (int)args[1], (int)args[2], args[3]);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic8484.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.arguments;
2 |
3 |
4 | /**
5 | * Created by weishu on 17/11/14.
6 | */
7 | public class ArgStatic8484 extends AbsArgStaticCase {
8 | @Override
9 | protected void makeCall(long... args) {
10 | ArgumentTarget.arg4(args[0], (int)args[1], args[2], (int)args[3]);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic8844.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.arguments;
2 |
3 |
4 | /**
5 | * Created by weishu on 17/11/14.
6 | */
7 | public class ArgStatic8844 extends AbsArgStaticCase {
8 | @Override
9 | protected void makeCall(long... args) {
10 | ArgumentTarget.arg4(args[0], args[1], (int)args[2], (int)args[3]);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic8444.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.arguments;
2 |
3 |
4 | /**
5 | * Created by weishu on 17/11/14.
6 | */
7 | public class ArgStatic8444 extends AbsArgStaticCase {
8 | @Override
9 | protected void makeCall(long... args) {
10 | ArgumentTarget.arg4(args[0], (int)args[1], (int)args[2], (int)args[3]);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic4444.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.arguments;
2 |
3 |
4 | /**
5 | * Created by weishu on 17/11/14.
6 | */
7 | public class ArgStatic4444 extends AbsArgStaticCase {
8 | @Override
9 | protected void makeCall(long... args) {
10 | ArgumentTarget.arg4((int)args[0], (int)args[1], (int)args[2], (int)args[3]);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic4448.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.arguments;
2 |
3 |
4 | /**
5 | * Created by weishu on 17/11/14.
6 | */
7 | public class ArgStatic4448 extends AbsArgStaticCase {
8 | @Override
9 | protected void makeCall(long... args) {
10 | ArgumentTarget.arg4((int) args[0], (int)args[1], (int)args[2], args[3]);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic4484.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.arguments;
2 |
3 |
4 | /**
5 | * Created by weishu on 17/11/14.
6 | */
7 | public class ArgStatic4484 extends AbsArgStaticCase {
8 | @Override
9 | protected void makeCall(long... args) {
10 | ArgumentTarget.arg4((int)args[0], (int)args[1], args[2], (int) args[3]);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic4844.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.arguments;
2 |
3 |
4 | /**
5 | * Created by weishu on 17/11/14.
6 | */
7 | public class ArgStatic4844 extends AbsArgStaticCase {
8 | @Override
9 | protected void makeCall(long... args) {
10 | ArgumentTarget.arg4((int) args[0], args[1], (int)args[2], (int)args[3]);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/library/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic88.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.arguments;
2 |
3 | /**
4 | * @author weishu
5 | * @date 17/11/14.
6 | */
7 |
8 | public class ArgStatic88 extends AbsArgStaticCase {
9 |
10 | @Override
11 | protected void makeCall(long... args) {
12 | super.makeCall(args);
13 | ArgumentTarget.arg2(args[0], args[1]);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/library/src/main/java/com/taobao/android/dexposed/utility/NeverCalled.java:
--------------------------------------------------------------------------------
1 | package com.taobao.android.dexposed.utility;
2 |
3 | import android.util.Log;
4 |
5 | /**
6 | * This Class is used for get the art_quick_to_interpreter_bridge address
7 | * Do not call this forever!!!
8 | */
9 | public class NeverCalled {
10 | private void fake(int a) {
11 | Log.i(getClass().getSimpleName(), a + "Do not inline me!!");
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/test/java/me/weishu/epic/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | //package me.weishu.epic;
2 | //
3 | //import org.junit.Test;
4 | //
5 | //import static org.junit.Assert.*;
6 | //
7 | ///**
8 | // * Example local unit test, which will execute on the development machine (host).
9 | // *
10 | // * @see Testing documentation
11 | // */
12 | //public class ExampleUnitTest {
13 | // @Test
14 | // public void addition_isCorrect() throws Exception {
15 | // assertEquals(4, 2 + 2);
16 | // }
17 | //}
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic444.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.arguments;
2 |
3 | import android.util.Log;
4 |
5 | import java.util.Arrays;
6 |
7 | /**
8 | * Created by weishu on 17/11/14.
9 | */
10 |
11 | public class ArgStatic444 extends AbsArgStaticCase {
12 |
13 | @Override
14 | protected void makeCall(long... args) {
15 | Log.i("mylog", "make call args:" + Arrays.toString(args));
16 | ArgumentTarget.arg3((int) args[0], (int) args[1], (int) args[2]);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/custom/Case15_StopJit.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.custom;
2 |
3 | import me.weishu.epic.art.EpicNative;
4 |
5 | /**
6 | * Created by weishu on 17/12/13.
7 | */
8 |
9 | public class Case15_StopJit implements Case {
10 | long cookie;
11 | @Override
12 | public void hook() {
13 | cookie = EpicNative.stopJit();
14 | }
15 |
16 | @Override
17 | public boolean validate(Object... args) {
18 | EpicNative.startJit(cookie);
19 | return true;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/MainApplication.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 |
6 | /**
7 | * Created by weishu on 17/10/31.
8 | */
9 | public class MainApplication extends Application {
10 |
11 | private static Context sContext;
12 |
13 | public static Context getAppContext() {
14 | return sContext;
15 | }
16 |
17 | @Override
18 | protected void attachBaseContext(Context base) {
19 | super.attachBaseContext(base);
20 |
21 | sContext = base;
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # replace
4 | patreon: weishu
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: http://paypal.me/virtualxposed
13 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/TestSuite.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | /**
7 | * Created by weishu on 17/11/13.
8 | */
9 |
10 | public class TestSuite {
11 | private String name;
12 | private List cases = new ArrayList<>();
13 |
14 | public TestSuite(String name) {
15 | this.name = name;
16 | }
17 |
18 | public void addCase(TestCase caze) {
19 | cases.add(caze);
20 | }
21 |
22 | public String getName() {
23 | return name;
24 | }
25 |
26 | public List getAllCases() {
27 | return cases;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/custom/Case14_GC.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.custom;
2 |
3 | import de.robv.android.xposed.DexposedBridge;
4 |
5 | import me.weishu.epic.samples.tests.LogMethodHook;
6 | import me.weishu.epic.samples.tests.returntype.ReturnTypeTarget;
7 |
8 | /**
9 | * Created by weishu on 17/12/13.
10 | */
11 |
12 | public class Case14_GC implements Case {
13 | @Override
14 | public void hook() {
15 | DexposedBridge.findAndHookMethod(ReturnTypeTarget.class, "returnString", String.class, new LogMethodHook());
16 | System.gc();
17 | }
18 |
19 | @Override
20 | public boolean validate(Object... args) {
21 | return "123".equals(ReturnTypeTarget.returnString(new String("123")));
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | ## Project-wide Gradle settings.
2 | #
3 | # For more details on how to configure your build environment visit
4 | # http://www.gradle.org/docs/current/userguide/build_environment.html
5 | #
6 | # Specifies the JVM arguments used for the daemon process.
7 | # The setting is particularly useful for tweaking memory settings.
8 | # Default value: -Xmx1024m -XX:MaxPermSize=256m
9 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
10 | #
11 | # When configured, Gradle will run in incubating parallel mode.
12 | # This option should only be used with decoupled projects. More details, visit
13 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
14 | # org.gradle.parallel=true
15 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/custom/Case13_FastNative.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.custom;
2 |
3 | import android.util.Log;
4 |
5 | import java.lang.reflect.Method;
6 |
7 | import de.robv.android.xposed.XposedHelpers;
8 |
9 | /**
10 | * Created by weishu on 17/12/13.
11 | */
12 |
13 | public class Case13_FastNative implements Case {
14 |
15 | @Override
16 | public void hook() {
17 | // DexposedBridge.findAndHookMethod(Target.class, "longRunMethod", new LogMethodHook());
18 | final Method invoke = XposedHelpers.findMethodExact(Method.class, "invoke", Object.class, Object[].class);
19 | Log.i("mylog", "invole: " + invoke);
20 | }
21 |
22 | @Override
23 | public boolean validate(Object... args) {
24 | new Target().longRunMethod();
25 | return true;
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/CallingConventationTest.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests;
2 |
3 | import me.weishu.epic.samples.tests.arguments.ArgumentTarget;
4 |
5 | /**
6 | * Created by weishu on 17/11/15.
7 | */
8 | public class CallingConventationTest {
9 |
10 | public static void longParams1() {
11 | // r0 = ArtMethod.this
12 | // r1 = 4
13 | // r2 = 8
14 | // r3 = parital 12
15 | // sp + 16 = partial 12
16 | ArgumentTarget.arg3(4, 8, 12L);
17 | }
18 |
19 | public static void longParams2() {
20 | // r0 = ArtMethod
21 | // r1, r2 = 1024L
22 | // r3 = bbcc1122
23 | // sp + 16 = 0xffaa
24 | ArgumentTarget.arg2(1024L, 0xffaabbcc1122L);
25 | }
26 |
27 | public static void longParams3() {
28 | ArgumentTarget.arg2(123, 0xffaabbcc1122L);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/me/weishu/epic/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | //package me.weishu.epic;
2 | //
3 | //import android.content.Context;
4 | //import android.support.test.InstrumentationRegistry;
5 | //import android.support.test.runner.AndroidJUnit4;
6 | //
7 | //import org.junit.Test;
8 | //import org.junit.runner.RunWith;
9 | //
10 | //import static junit.framework.Assert.assertEquals;
11 | //import static org.junit.Assert.*;
12 | //
13 | ///**
14 | // * Instrumentation test, which will execute on an Android device.
15 | // *
16 | // * @see Testing documentation
17 | // */
18 | //@RunWith(AndroidJUnit4.class)
19 | //public class ExampleInstrumentedTest {
20 | // @Test
21 | // public void useAppContext() throws Exception {
22 | // // Context of the app under test.
23 | // Context appContext = InstrumentationRegistry.getTargetContext();
24 | //
25 | // assertEquals("me.weishu.epic", appContext.getPackageName());
26 | // }
27 | //}
28 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/parent_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
14 |
15 |
25 |
26 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/weishu/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 30
5 | defaultConfig {
6 | applicationId "me.weishu.epic.samples"
7 | minSdkVersion 21
8 | targetSdkVersion 30
9 | versionCode 1
10 | versionName "1.0"
11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
12 | externalNativeBuild {
13 | ndkBuild {
14 | abiFilters "armeabi-v7a", "x86"
15 | }
16 | }
17 | }
18 | buildTypes {
19 | release {
20 | minifyEnabled false
21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
22 | }
23 | }
24 | sourceSets {
25 | main {
26 | jniLibs.srcDirs = ['libs']
27 | }
28 | }
29 | lintOptions {
30 | abortOnError false
31 | }
32 | }
33 |
34 | dependencies {
35 | implementation fileTree(dir: 'libs', include: ['*.jar'])
36 | implementation project(":library")
37 | implementation 'com.qmuiteam:qmui:1.0.4'
38 | }
39 |
--------------------------------------------------------------------------------
/library/src/main/java/com/taobao/android/dexposed/utility/Logger.java:
--------------------------------------------------------------------------------
1 | package com.taobao.android.dexposed.utility;
2 |
3 | import android.util.Log;
4 |
5 | /**
6 | * Created by weishu on 17/11/10.
7 | */
8 | public class Logger {
9 |
10 | private static final boolean DEBUG = Debug.DEBUG;
11 |
12 | public static final String preFix = "epic.";
13 |
14 | public static void i(String tag, String msg) {
15 | if (DEBUG) {
16 | Log.i(preFix + tag, msg);
17 | }
18 | }
19 |
20 | public static void d(String tagSuffix, String msg) {
21 | if (DEBUG) {
22 | Log.d(preFix + tagSuffix, msg);
23 | }
24 | }
25 |
26 | public static void w(String tag, String msg) {
27 | Log.w(preFix + tag, msg);
28 | }
29 |
30 | public static void e(String tag, String msg) {
31 | if (DEBUG) {
32 | Log.e(preFix + tag, msg);
33 | }
34 | }
35 |
36 | public static void e(String tag, String msg, Throwable e) {
37 | if (DEBUG) {
38 | Log.e(preFix + tag, msg, e);
39 | }
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/TestCase.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests;
2 |
3 | import android.util.Log;
4 | import android.widget.Toast;
5 |
6 | import me.weishu.epic.samples.MainApplication;
7 |
8 | /**
9 | * Created by weishu on 17/11/13.
10 | */
11 | public abstract class TestCase {
12 |
13 | private static final String TAG = "TestCase";
14 |
15 | protected String name;
16 | public TestCase(String name) {
17 | this.name = name;
18 | }
19 |
20 | public abstract void test();
21 |
22 | public abstract boolean predicate();
23 |
24 | public boolean validate() {
25 | boolean validate;
26 | try {
27 | validate = predicate();
28 | } catch (Throwable e) {
29 | validate = false;
30 | Log.e(TAG, "error happened:", e);
31 | }
32 | if (!validate) {
33 | Toast.makeText(MainApplication.getAppContext(), "测试不通过。", Toast.LENGTH_SHORT).show();
34 | }
35 | return validate;
36 | }
37 |
38 | public String getName() {
39 | return name;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/custom/Case7.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.custom;
2 |
3 | import android.util.Log;
4 |
5 | import java.util.Arrays;
6 |
7 | import de.robv.android.xposed.DexposedBridge;
8 | import de.robv.android.xposed.XC_MethodHook;
9 |
10 | /**
11 | * Created by weishu on 17/11/8.
12 | */
13 |
14 | public class Case7 implements Case {
15 |
16 | private static final String TAG = "Case7";
17 |
18 | @Override
19 | public void hook() {
20 | Log.i(TAG, "hook test1");
21 | DexposedBridge.findAndHookMethod(Target.class, "test1", Object.class, int.class, new XC_MethodHook() {
22 | @Override
23 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
24 | Log.i(TAG, "before add hooked:" + Arrays.toString(param.args));
25 | param.setResult(4);
26 | super.beforeHookedMethod(param);
27 | }
28 | });
29 |
30 | }
31 |
32 | @Override
33 | public boolean validate(Object... args) {
34 | Target t = new Target();
35 | t.test1(t, 123);
36 | return true;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/returntype/LongType.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.returntype;
2 |
3 | import de.robv.android.xposed.DexposedBridge;
4 |
5 | import me.weishu.epic.samples.tests.LogMethodHook;
6 | import me.weishu.epic.samples.tests.TestCase;
7 |
8 | /**
9 | * Created by weishu on 17/11/13.
10 | */
11 | public class LongType extends TestCase {
12 |
13 | final long returnType = Long.MAX_VALUE / 2;
14 | final long returnTypeModified = returnType - 1;
15 |
16 | public LongType() {
17 | super("Long");
18 | }
19 |
20 | @Override
21 | public void test() {
22 |
23 |
24 | DexposedBridge.findAndHookMethod(ReturnTypeTarget.class, "returnLong", long.class, new LogMethodHook() {
25 | @Override
26 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
27 | param.setResult(returnTypeModified);
28 | super.beforeHookedMethod(param);
29 | }
30 | });
31 | }
32 |
33 | @Override
34 | public boolean predicate() {
35 | return ReturnTypeTarget.returnLong(returnType) == returnTypeModified;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/child_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
15 |
21 |
22 |
30 |
31 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/returntype/CharType.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.returntype;
2 |
3 | import de.robv.android.xposed.DexposedBridge;
4 |
5 | import me.weishu.epic.samples.tests.LogMethodHook;
6 | import me.weishu.epic.samples.tests.TestCase;
7 |
8 | /**
9 | * Created by weishu on 17/11/13.
10 | */
11 | public class CharType extends TestCase {
12 |
13 | final char returnType = Character.MAX_VALUE;
14 | final char returnTypeModified = returnType - 1;
15 |
16 | public CharType() {
17 | super("Char");
18 | }
19 |
20 | @Override
21 | public void test() {
22 |
23 |
24 | DexposedBridge.findAndHookMethod(ReturnTypeTarget.class, "returnChar", char.class, new LogMethodHook() {
25 | @Override
26 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
27 | param.setResult(returnTypeModified);
28 | super.beforeHookedMethod(param);
29 | }
30 | });
31 | }
32 |
33 | @Override
34 | public boolean predicate() {
35 | return ReturnTypeTarget.returnChar(returnType) == returnTypeModified;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/returntype/FloatType.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.returntype;
2 |
3 | import de.robv.android.xposed.DexposedBridge;
4 |
5 | import me.weishu.epic.samples.tests.LogMethodHook;
6 | import me.weishu.epic.samples.tests.TestCase;
7 |
8 | /**
9 | * Created by weishu on 17/11/13.
10 | */
11 | public class FloatType extends TestCase {
12 |
13 | final float returnType = 12545.212f;
14 | final float returnTypeModified = returnType - 1;
15 |
16 | public FloatType() {
17 | super("Float");
18 | }
19 |
20 | @Override
21 | public void test() {
22 |
23 |
24 | DexposedBridge.findAndHookMethod(ReturnTypeTarget.class, "returnFloat", float.class, new LogMethodHook() {
25 | @Override
26 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
27 | param.setResult(returnTypeModified);
28 | super.beforeHookedMethod(param);
29 | }
30 | });
31 | }
32 |
33 | @Override
34 | public boolean predicate() {
35 | return ReturnTypeTarget.returnFloat(returnType) == returnTypeModified;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/returntype/ShortType.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.returntype;
2 |
3 | import de.robv.android.xposed.DexposedBridge;
4 |
5 | import me.weishu.epic.samples.tests.LogMethodHook;
6 | import me.weishu.epic.samples.tests.TestCase;
7 |
8 | /**
9 | * Created by weishu on 17/11/13.
10 | */
11 | public class ShortType extends TestCase {
12 |
13 | final short returnType = Short.MAX_VALUE / 2;
14 | final short returnTypeModified = returnType - 1;
15 |
16 | public ShortType() {
17 | super("Short");
18 | }
19 |
20 | @Override
21 | public void test() {
22 |
23 | DexposedBridge.findAndHookMethod(ReturnTypeTarget.class, "returnShort", short.class, new LogMethodHook() {
24 | @Override
25 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
26 | param.setResult(returnTypeModified);
27 | super.beforeHookedMethod(param);
28 | }
29 | });
30 | }
31 |
32 | @Override
33 | public boolean predicate() {
34 | return ReturnTypeTarget.returnShort(returnType) == returnTypeModified;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/custom/CaseManager.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.custom;
2 |
3 | import java.util.Map;
4 | import java.util.Set;
5 | import java.util.concurrent.ConcurrentHashMap;
6 |
7 | /**
8 | * Created by weishu on 17/11/6.
9 | */
10 |
11 | public class CaseManager {
12 |
13 | private static volatile CaseManager INSTANCE = new CaseManager();
14 |
15 | private Map, Case> caseMap = new ConcurrentHashMap<>();
16 |
17 | public static synchronized CaseManager getInstance() {
18 | return INSTANCE;
19 | }
20 |
21 | public synchronized Case getCase(Class> clazz) {
22 | Case caze = caseMap.get(clazz);
23 | if (caze != null) {
24 | return caze;
25 | } else {
26 | try {
27 | caze = (Case) clazz.newInstance();
28 | caseMap.put(clazz, caze);
29 | } catch (Throwable e) {
30 | throw new RuntimeException("can not get case !!", e);
31 | }
32 | }
33 | return caze;
34 | }
35 |
36 | public synchronized Set> getCases() {
37 | return caseMap.keySet();
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/returntype/BooleanType.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.returntype;
2 |
3 | import de.robv.android.xposed.DexposedBridge;
4 |
5 | import me.weishu.epic.samples.tests.LogMethodHook;
6 | import me.weishu.epic.samples.tests.TestCase;
7 |
8 | /**
9 | * Created by weishu on 17/11/13.
10 | */
11 | public class BooleanType extends TestCase {
12 |
13 | final boolean returnType = Boolean.FALSE;
14 | final boolean returnTypeModified = !returnType;
15 |
16 | public BooleanType() {
17 | super("Boolean");
18 | }
19 |
20 | @Override
21 | public void test() {
22 |
23 | DexposedBridge.findAndHookMethod(ReturnTypeTarget.class, "returnBoolean", boolean.class, new LogMethodHook() {
24 | @Override
25 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
26 | param.setResult(returnTypeModified);
27 | super.beforeHookedMethod(param);
28 | }
29 | });
30 | }
31 |
32 | @Override
33 | public boolean predicate() {
34 | return ReturnTypeTarget.returnBoolean(returnType) == returnTypeModified;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/returntype/DoubleType.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.returntype;
2 |
3 | import de.robv.android.xposed.DexposedBridge;
4 |
5 | import me.weishu.epic.samples.tests.LogMethodHook;
6 | import me.weishu.epic.samples.tests.TestCase;
7 |
8 | /**
9 | * Created by weishu on 17/11/13.
10 | */
11 | public class DoubleType extends TestCase {
12 |
13 | final double returnType = 12343748.12435;
14 | final double returnTypeModified = returnType - 1;
15 |
16 | public DoubleType() {
17 | super("Double");
18 | }
19 |
20 | @Override
21 | public void test() {
22 |
23 |
24 | DexposedBridge.findAndHookMethod(ReturnTypeTarget.class, "returnDouble", double.class, new LogMethodHook() {
25 | @Override
26 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
27 | param.setResult(returnTypeModified);
28 | super.beforeHookedMethod(param);
29 | }
30 | });
31 | }
32 |
33 | @Override
34 | public boolean predicate() {
35 | return ReturnTypeTarget.returnDouble(returnType) == returnTypeModified;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/returntype/StringType.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.returntype;
2 |
3 | import de.robv.android.xposed.DexposedBridge;
4 |
5 | import me.weishu.epic.samples.tests.LogMethodHook;
6 | import me.weishu.epic.samples.tests.TestCase;
7 |
8 | /**
9 | * Created by weishu on 17/11/13.
10 | */
11 | public class StringType extends TestCase {
12 |
13 | final String returnType = "123455";
14 | final String returnTypeModified = "784fsgiulfodsg";
15 |
16 | public StringType() {
17 | super("String");
18 | }
19 |
20 | @Override
21 | public void test() {
22 |
23 |
24 | DexposedBridge.findAndHookMethod(ReturnTypeTarget.class, "returnString", String.class, new LogMethodHook() {
25 | @Override
26 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
27 | param.setResult(returnTypeModified);
28 | super.beforeHookedMethod(param);
29 | }
30 | });
31 | }
32 |
33 | @Override
34 | public boolean predicate() {
35 | return ReturnTypeTarget.returnString(returnType).equals(returnTypeModified);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/custom/Case10_Default_Constructor.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.custom;
2 |
3 | import android.util.Log;
4 |
5 | import java.lang.reflect.Constructor;
6 |
7 | import de.robv.android.xposed.DexposedBridge;
8 | import de.robv.android.xposed.XC_MethodHook;
9 | import de.robv.android.xposed.XposedHelpers;
10 |
11 | /**
12 | * Created by weishu on 17/11/13.
13 | */
14 |
15 | public class Case10_Default_Constructor implements Case {
16 |
17 | private static final String TAG = "Case10_Default_Constructor";
18 |
19 | @Override
20 | public void hook() {
21 | Constructor> constructor = XposedHelpers.findConstructorExact(Target.class);
22 | DexposedBridge.hookMethod(constructor, new XC_MethodHook() {
23 | @Override
24 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
25 | super.beforeHookedMethod(param);
26 | Log.i(TAG, "Target constructor called");
27 | }
28 | });
29 | }
30 |
31 | @Override
32 | public boolean validate(Object... args) {
33 | new Target();
34 | return true;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/returntype/IntType.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.returntype;
2 |
3 | import de.robv.android.xposed.DexposedBridge;
4 |
5 | import me.weishu.epic.samples.tests.LogMethodHook;
6 | import me.weishu.epic.samples.tests.TestCase;
7 |
8 | /**
9 | * Created by weishu on 17/11/13.
10 | */
11 | public class IntType extends TestCase {
12 |
13 | final int returnType = Integer.MAX_VALUE;
14 | final int returnTypeModified = returnType - 1;
15 |
16 | public IntType() {
17 | super("Int");
18 | }
19 |
20 | @Override
21 | public void test() {
22 |
23 |
24 | DexposedBridge.findAndHookMethod(ReturnTypeTarget.class, "returnInt", int.class, new LogMethodHook() {
25 | @Override
26 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
27 | param.setResult(returnTypeModified);
28 | super.beforeHookedMethod(param);
29 | }
30 | });
31 | }
32 |
33 | @Override
34 | public boolean predicate() {
35 | final int i = ReturnTypeTarget.returnInt(returnType);
36 | return i == returnTypeModified;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/returntype/ByteType.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.returntype;
2 |
3 | import de.robv.android.xposed.DexposedBridge;
4 |
5 | import me.weishu.epic.samples.tests.LogMethodHook;
6 | import me.weishu.epic.samples.tests.TestCase;
7 |
8 | /**
9 | * Created by weishu on 17/11/13.
10 | */
11 | public class ByteType extends TestCase {
12 |
13 | final byte returnType = Byte.MAX_VALUE;
14 | final byte returnTypeModified = Byte.MAX_VALUE - 1;
15 |
16 | public ByteType() {
17 | super("Byte");
18 | }
19 |
20 | @Override
21 | public void test() {
22 |
23 | DexposedBridge.findAndHookMethod(ReturnTypeTarget.class, "returnByte", byte.class, new LogMethodHook() {
24 | @Override
25 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
26 | param.setResult(returnTypeModified);
27 | super.beforeHookedMethod(param);
28 | }
29 | });
30 | }
31 |
32 | @Override
33 | public boolean predicate() {
34 | final byte raw = ReturnTypeTarget.returnByte(returnType);
35 | return raw == returnTypeModified;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/returntype/CustomType.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.returntype;
2 |
3 | import de.robv.android.xposed.DexposedBridge;
4 |
5 | import me.weishu.epic.samples.tests.LogMethodHook;
6 | import me.weishu.epic.samples.tests.TestCase;
7 |
8 | /**
9 | * Created by weishu on 17/11/13.
10 | */
11 | public class CustomType extends TestCase {
12 |
13 | final ReturnTypeTarget returnType = new ReturnTypeTarget();
14 | final ReturnTypeTarget returnTypeModified = new ReturnTypeTarget();
15 |
16 | public CustomType() {
17 | super("Custom");
18 | }
19 |
20 | @Override
21 | public void test() {
22 |
23 | DexposedBridge.findAndHookMethod(ReturnTypeTarget.class, "returnCustom", ReturnTypeTarget.class, new LogMethodHook() {
24 | @Override
25 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
26 | param.setResult(returnTypeModified);
27 | super.beforeHookedMethod(param);
28 | }
29 | });
30 | }
31 |
32 | @Override
33 | public boolean predicate() {
34 | return ReturnTypeTarget.returnCustom(returnType) == returnTypeModified;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/returntype/StringArrayType.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.returntype;
2 |
3 | import de.robv.android.xposed.DexposedBridge;
4 |
5 | import me.weishu.epic.samples.tests.LogMethodHook;
6 | import me.weishu.epic.samples.tests.TestCase;
7 |
8 | /**
9 | * Created by weishu on 17/11/13.
10 | */
11 | public class StringArrayType extends TestCase {
12 |
13 | final String[] returnType = new String[]{"123", "456"};
14 | final String[] returnTypeModified = new String[]{"123", "456", "678"};
15 |
16 | public StringArrayType() {
17 | super("StringArray");
18 | }
19 |
20 | @Override
21 | public void test() {
22 |
23 |
24 | DexposedBridge.findAndHookMethod(ReturnTypeTarget.class, "returnStringArray", String[].class, new LogMethodHook() {
25 | @Override
26 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
27 | param.setResult(returnTypeModified);
28 | super.beforeHookedMethod(param);
29 | }
30 | });
31 | }
32 |
33 | @Override
34 | public boolean predicate() {
35 | return ReturnTypeTarget.returnStringArray(returnType) == returnTypeModified;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/custom/Case3.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.custom;
2 |
3 | import android.util.Log;
4 |
5 | import de.robv.android.xposed.DexposedBridge;
6 | import de.robv.android.xposed.XC_MethodHook;
7 |
8 | /**
9 | * Created by weishu on 17/11/6.
10 | */
11 |
12 | public class Case3 implements Case {
13 | private static final String TAG = "Case3";
14 |
15 | @Override
16 | public void hook() {
17 | DexposedBridge.findAndHookMethod(System.class, "currentTimeMillis", new XC_MethodHook() {
18 | @Override
19 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
20 | super.beforeHookedMethod(param);
21 | Log.i("mylog", "before currentTimeMillis");
22 | }
23 |
24 | @Override
25 | protected void afterHookedMethod(MethodHookParam param) throws Throwable {
26 | super.afterHookedMethod(param);
27 | Log.i("mylog", "after currentTimeMillis");
28 | }
29 | });
30 | }
31 |
32 | @Override
33 | public boolean validate(Object... args) {
34 | Log.i(TAG, "currentTimeMillis: " + System.currentTimeMillis());
35 | return true;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/custom/Case8_Activity_onCreate.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.custom;
2 |
3 | import android.os.Bundle;
4 | import android.util.Log;
5 |
6 | import de.robv.android.xposed.DexposedBridge;
7 | import de.robv.android.xposed.XC_MethodHook;
8 | import me.weishu.epic.samples.MainActivity;
9 |
10 | /**
11 | * Created by weishu on 17/11/9.
12 | */
13 |
14 | public class Case8_Activity_onCreate implements Case {
15 | private static final String TAG = "Case8_Activity_onCreate";
16 |
17 | @Override
18 | public void hook() {
19 | DexposedBridge.findAndHookMethod(MainActivity.class, "onCreate", Bundle.class, new XC_MethodHook() {
20 | @Override
21 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
22 | super.beforeHookedMethod(param);
23 | Log.i(TAG, "before hooked");
24 | if (param.args[0] == null) {
25 | param.args[0] = new Bundle();
26 | }
27 | Bundle b = (Bundle) param.args[0];
28 | Log.i(TAG, "bundle: " + param.args[0]);
29 | b.putString("hehe", "hacked");
30 | }
31 | });
32 | }
33 |
34 | @Override
35 | public boolean validate(Object... args) {
36 | return true;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/invoketype/InvokeConstructor.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.invoketype;
2 |
3 | import de.robv.android.xposed.DexposedBridge;
4 | import de.robv.android.xposed.XposedHelpers;
5 | import me.weishu.epic.samples.tests.LogMethodHook;
6 | import me.weishu.epic.samples.tests.TestCase;
7 |
8 | /**
9 | * Created by weishu on 17/11/14.
10 | */
11 |
12 | public class InvokeConstructor extends TestCase {
13 |
14 | boolean callBefore = false;
15 | boolean callAfter = false;
16 | public InvokeConstructor() {
17 | super("Constructor");
18 | }
19 |
20 | @Override
21 | public void test() {
22 | DexposedBridge.hookMethod(XposedHelpers.findConstructorExact(InvokeTypeTarget.class), new LogMethodHook() {
23 | @Override
24 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
25 | super.beforeHookedMethod(param);
26 | callBefore = true;
27 | }
28 |
29 | @Override
30 | protected void afterHookedMethod(MethodHookParam param) throws Throwable {
31 | super.afterHookedMethod(param);
32 | callAfter = true;
33 | }
34 | });
35 | }
36 |
37 | @Override
38 | public boolean predicate() {
39 | new InvokeTypeTarget();
40 | return callBefore && callAfter;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/arguments/ArgStatic0.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.arguments;
2 |
3 | import de.robv.android.xposed.DexposedBridge;
4 | import de.robv.android.xposed.XC_MethodHook;
5 | import me.weishu.epic.samples.tests.LogMethodHook;
6 | import me.weishu.epic.samples.tests.TestCase;
7 |
8 | /**
9 | * @author weishu
10 | * @date 17/11/14.
11 | */
12 |
13 | public class ArgStatic0 extends TestCase {
14 |
15 | boolean beforeCalled = false;
16 | boolean afterCalled = false;
17 | public ArgStatic0() {
18 | super("ArgStatic0");
19 | }
20 |
21 | @Override
22 | public void test() {
23 | DexposedBridge.findAndHookMethod(ArgumentTarget.class, "arg0", new LogMethodHook() {
24 | @Override
25 | protected void beforeHookedMethod(XC_MethodHook.MethodHookParam param) throws Throwable {
26 | super.beforeHookedMethod(param);
27 | beforeCalled = true;
28 | }
29 |
30 | @Override
31 | protected void afterHookedMethod(MethodHookParam param) throws Throwable {
32 | super.afterHookedMethod(param);
33 | afterCalled = true;
34 | }
35 | });
36 | }
37 |
38 | @Override
39 | public boolean predicate() {
40 |
41 | ArgumentTarget.arg0();
42 |
43 | return beforeCalled && afterCalled;
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/LogMethodHook.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests;
2 |
3 | import android.util.Log;
4 |
5 | import java.util.Arrays;
6 |
7 | import de.robv.android.xposed.XC_MethodHook;
8 |
9 | /**
10 | * Created by weishu on 17/11/13.
11 | */
12 |
13 | public class LogMethodHook extends XC_MethodHook {
14 |
15 | @Override
16 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
17 | super.beforeHookedMethod(param);
18 | Log.i(getClass().getSimpleName(), "beforeHookedMethod() called with: param = [" + paramToString(param) + "]");
19 | }
20 |
21 | @Override
22 | protected void afterHookedMethod(MethodHookParam param) throws Throwable {
23 | super.afterHookedMethod(param);
24 | Log.i(getClass().getSimpleName(), "afterHookedMethod() called with: param = [" + paramToString(param) + "]");
25 | }
26 |
27 | private static String paramToString(MethodHookParam param) {
28 | StringBuilder sb = new StringBuilder(param.getClass().getSimpleName()).append("{");
29 | sb.append("method = ").append(param.method.getName()).append(", ");
30 | sb.append("this = ").append(param.thisObject).append(", ");
31 | sb.append("args = ").append(Arrays.toString(param.args)).append(",");
32 | sb.append("result = ").append(param.getResult()).append("}");
33 | return sb.toString();
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/custom/Case18_returnConst.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.custom;
2 |
3 | import android.util.Log;
4 |
5 | import java.util.Arrays;
6 |
7 | import de.robv.android.xposed.DexposedBridge;
8 | import de.robv.android.xposed.XC_MethodHook;
9 |
10 | /**
11 | * Created by weishu on 18/1/11.
12 | */
13 | public class Case18_returnConst implements Case {
14 | private static final String TAG = "Case18_returnConst";
15 | @Override
16 | public void hook() {
17 | DexposedBridge.findAndHookMethod(Target.class, "returnConst", int.class, new XC_MethodHook() {
18 | @Override
19 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
20 | super.beforeHookedMethod(param);
21 | param.setResult(124);
22 | Log.d(TAG, "beforeHookedMethod() called with: param = [" + Arrays.toString(param.args) + "]");
23 | }
24 |
25 | @Override
26 | protected void afterHookedMethod(MethodHookParam param) throws Throwable {
27 | super.afterHookedMethod(param);
28 | Log.d(TAG, "afterHookedMethod() called with: param = [" + Arrays.toString(param.args) + "]");
29 | }
30 | });
31 | }
32 |
33 | @Override
34 | public boolean validate(Object... args) {
35 |
36 | int i = new Target().returnConst(123);
37 | Log.i(TAG, "return : " + i);
38 | return (i == 124);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/returntype/VoidType.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.returntype;
2 |
3 | import android.util.Log;
4 |
5 | import de.robv.android.xposed.DexposedBridge;
6 | import de.robv.android.xposed.XC_MethodHook;
7 | import me.weishu.epic.samples.tests.TestCase;
8 |
9 | /**
10 | * Created by weishu on 17/11/13.
11 | */
12 |
13 | public class VoidType extends TestCase {
14 |
15 | private static final String TAG = "VoidType";
16 |
17 | boolean callBefore = false;
18 | boolean callAfter = false;
19 |
20 | public VoidType() {
21 | super("无返回值");
22 | }
23 |
24 | @Override
25 | public void test() {
26 | DexposedBridge.findAndHookMethod(ReturnTypeTarget.class, "returnVoid", new XC_MethodHook() {
27 | @Override
28 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
29 | super.beforeHookedMethod(param);
30 | callBefore = true;
31 | }
32 |
33 | @Override
34 | protected void afterHookedMethod(MethodHookParam param) throws Throwable {
35 | super.afterHookedMethod(param);
36 | callAfter = true;
37 | }
38 | });
39 | }
40 |
41 | @Override
42 | public boolean predicate() {
43 | ReturnTypeTarget.returnVoid();
44 |
45 | Log.i(TAG, "callBefore:" + callBefore + ", callAfter:" + callAfter);
46 | return callBefore && callAfter;
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/library/src/main/cpp/fake_dlfcn.h:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 avs333
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in all
11 | // copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | // SOFTWARE.
20 |
21 | #ifndef DEXPOSED_DLFCN_H
22 | #define DEXPOSED_DLFCN_H
23 |
24 | #include
25 | #include
26 | #include
27 |
28 | extern "C" {
29 |
30 | void *dlopen_ex(const char *filename, int flags);
31 |
32 | void *dlsym_ex(void *handle, const char *symbol);
33 |
34 | int dlclose_ex(void *handle);
35 |
36 | const char *dlerror_ex();
37 |
38 | };
39 | #endif //DEXPOSED_DLFCN_H
40 |
--------------------------------------------------------------------------------
/library/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/weishu/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 | -keepattributes Signature,SourceFile,LineNumberTable,Exceptions
27 |
28 | -keep class com.taobao.android.dexposed.** {*;}
29 | -keep class me.weishu.epic.art.** {*;}
30 |
31 | # delete log in release mode.
32 | -assumenosideeffects class com.taobao.android.dexposed.utility.Logger {
33 | public static void i(...);
34 | public static void w(...);
35 | public static void d(...);
36 | public static void e(...);
37 | }
38 |
39 | -assumenosideeffects class com.taobao.android.dexposed.utility.Debug {
40 | public static *** hexdump(...);
41 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/custom/Case4.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.custom;
2 |
3 | import android.text.TextUtils;
4 | import android.util.Log;
5 |
6 | import de.robv.android.xposed.DexposedBridge;
7 | import de.robv.android.xposed.XC_MethodHook;
8 |
9 | /**
10 | * Created by weishu on 17/11/6.
11 | */
12 |
13 | public class Case4 implements Case {
14 | private static final String TAG = "Case4";
15 |
16 | @Override
17 | public void hook() {
18 | DexposedBridge.findAndHookMethod(TextUtils.class, "equals", CharSequence.class, CharSequence.class, new XC_MethodHook() {
19 | @Override
20 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
21 | Log.i(TAG, "beforeHookedMethod: this:" + param.thisObject, new RuntimeException("stack"));
22 | Log.i(TAG, "beforeHookedMethod: String1:" + param.args[0]);
23 | Log.i(TAG, "beforeHookedMethod: String2:" + param.args[1]);
24 |
25 | param.setResult(false);
26 |
27 | super.beforeHookedMethod(param);
28 | }
29 |
30 | @Override
31 | protected void afterHookedMethod(MethodHookParam param) throws Throwable {
32 | super.afterHookedMethod(param);
33 | }
34 | });
35 | }
36 |
37 | @Override
38 | public boolean validate(Object... args) {
39 | String a1 = new String("123");
40 | String a2 = new String("123");
41 |
42 | Log.i(TAG, " '123' equals '123' ? " + TextUtils.equals(a1, a2));
43 | return true;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/library/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 31
5 |
6 | defaultConfig {
7 | minSdkVersion 21
8 | targetSdkVersion 31
9 | versionCode 1
10 | versionName "1.0"
11 |
12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
13 |
14 | ndk {
15 | // Specifies the ABI configurations of your native
16 | // libraries Gradle should build and package with your APK.
17 | abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
18 | }
19 | externalNativeBuild {
20 | cmake {
21 | cppFlags "-std=c++11"
22 | // arguments "-DANDROID_TOOLCHAIN=gcc"
23 | }
24 | }
25 | }
26 | buildTypes {
27 | release {
28 | minifyEnabled false
29 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
30 | }
31 | }
32 | externalNativeBuild {
33 | cmake {
34 | path 'src/main/cpp/CMakeLists.txt'
35 | }
36 | }
37 | lintOptions {
38 | abortOnError false
39 | }
40 | }
41 |
42 | dependencies {
43 | implementation fileTree(dir: 'libs', include: ['*.jar'])
44 | implementation "com.github.tiann:FreeReflection:3.1.0"
45 | api 'me.weishu.exposed:exposed-xposedapi:0.4.5'
46 | }
47 |
48 | apply plugin: 'com.github.panpf.bintray-publish'
49 |
50 | publish {
51 | userOrg = 'twsxtd'//
52 | groupId = 'me.weishu'
53 | artifactId = 'epic'
54 | publishVersion = '0.11.1'
55 | desc = 'Android Java AOP Method Hook (Dexposed on ART)'
56 | website = 'https://github.com/tiann/epic'
57 | }
58 |
59 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/custom/Case11_SuspendAll.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.custom;
2 |
3 | import android.os.Build;
4 | import android.os.SystemClock;
5 | import android.util.Log;
6 |
7 | import java.util.concurrent.Executor;
8 | import java.util.concurrent.Executors;
9 |
10 | import me.weishu.epic.art.EpicNative;
11 |
12 | /**
13 | * Created by weishu on 17/11/18.
14 | */
15 |
16 | public class Case11_SuspendAll implements Case {
17 |
18 | private static final String TAG = "Case11_SuspendAll";
19 |
20 | @Override
21 | public void hook() {
22 | Executor executor = Executors.newCachedThreadPool();
23 | for (int i = 0; i < 5; i++) {
24 | executor.execute(new Runnable() {
25 | @Override
26 | public void run() {
27 | int j = 0;
28 | while (true) {
29 | Log.i(TAG, "I am:" + Thread.currentThread().getName() + ", count:" + (j++));
30 | SystemClock.sleep(1000);
31 | if (j > 3) {
32 | break;
33 | }
34 | }
35 | }
36 | });
37 | }
38 | }
39 |
40 | @Override
41 | public boolean validate(Object... args) {
42 | if (Build.VERSION.SDK_INT < 24) {
43 | Log.i(TAG, "resume/suspend only support Android N+ now.");
44 | return false;
45 | }
46 | long cookie = EpicNative.suspendAll();
47 | for (Thread thread : Thread.getAllStackTraces().keySet()) {
48 | Log.i(TAG, thread.getName() + " status:" + thread.getState());
49 | }
50 | EpicNative.resumeAll(cookie);
51 |
52 | return true;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/custom/Case2.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.custom;
2 |
3 | /**
4 | * Created by weishu on 17/11/6.
5 | */
6 |
7 | public class Case2 implements Case{
8 | private static final String TAG = "Case2";
9 |
10 | @Override
11 | public void hook() {
12 | // DexposedBridge.findAndHookMethod(Target.class, "add", int.class, int.class, new XC_MethodHook() {
13 | // @Override
14 | // protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
15 | // Log.i(TAG, "before add hooked:" + Arrays.toString(param.args));
16 | // param.setResult(4);
17 | // super.beforeHookedMethod(param);
18 | // }
19 | // });
20 | //
21 | // DexposedBridge.findAndHookMethod(Target.class, "test4", int.class, new XC_MethodHook() {
22 | // @Override
23 | // protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
24 | // super.beforeHookedMethod(param);
25 | // Log.i("mylog", "before", new RuntimeException("stack"));
26 | // Log.i("mylog", "this:" + param.thisObject);
27 | // Log.i("mylog", "method:" + param.method);
28 | // Log.i("mylog", "args:" + Arrays.toString(param.args));
29 | //
30 | // }
31 | //
32 | // @Override
33 | // protected void afterHookedMethod(MethodHookParam param) throws Throwable {
34 | // super.afterHookedMethod(param);
35 | // Log.i("mylog", "after");
36 | // }
37 | // });
38 | }
39 |
40 | @Override
41 | public boolean validate(Object... args) {
42 | // Log.i(TAG, "1 + 2: " + Target.add(1, 2));
43 | // return Target.add(1, 2) == 4;
44 | Target.validate();
45 | return true;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/library/src/main/cpp/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # For more information about using CMake with Android Studio, read the
2 | # documentation: https://d.android.com/studio/projects/add-native-code.html
3 |
4 | # Sets the minimum version of CMake required to build the native library.
5 |
6 | cmake_minimum_required(VERSION 3.4.1)
7 |
8 | # Creates and names a library, sets it as either STATIC
9 | # or SHARED, and provides the relative paths to its source code.
10 | # You can define multiple libraries, and CMake builds them for you.
11 | # Gradle automatically packages shared libraries with your APK.
12 |
13 | set (SRC_LIST epic.cpp fake_dlfcn.cpp art.cpp)
14 | add_library( # Sets the name of the library.
15 | epic
16 |
17 | # Sets the library as a shared library.
18 | SHARED
19 |
20 | # Provides a relative path to your source file(s).
21 | ${SRC_LIST})
22 |
23 | # Searches for a specified prebuilt library and stores the path as a
24 | # variable. Because CMake includes system libraries in the search path by
25 | # default, you only need to specify the name of the public NDK library
26 | # you want to add. CMake verifies that the library exists before
27 | # completing its build.
28 |
29 | find_library( # Sets the name of the path variable.
30 | log-lib
31 |
32 | # Specifies the name of the NDK library that
33 | # you want CMake to locate.
34 | log )
35 |
36 | # Specifies libraries CMake should link to your target library. You
37 | # can link multiple libraries, such as libraries you define in this
38 | # build script, prebuilt third-party libraries, or system libraries.
39 |
40 | target_link_libraries( # Specifies the target library.
41 | epic
42 |
43 | # Links the target library to the log library
44 | # included in the NDK.
45 | ${log-lib} )
46 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/returntype/ReturnTypeTarget.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.returntype;
2 |
3 | import android.util.Log;
4 |
5 | /**
6 | * Created by weishu on 17/11/13.
7 | */
8 |
9 | public class ReturnTypeTarget {
10 | private static final String TAG = "ReturnTypeTarget";
11 |
12 | public static void returnVoid() {
13 | Log.d(TAG, "returnVoid() called");
14 | }
15 |
16 | public static byte returnByte(byte b) {
17 | Log.i(TAG, "returnByte() called");
18 | return b;
19 | }
20 |
21 | public static char returnChar(char c) {
22 | Log.i(TAG, "returnChar() called");
23 | return c;
24 | }
25 |
26 | public static short returnShort(short s) {
27 | Log.i(TAG, "returnShort() called");
28 | return s;
29 | }
30 |
31 | public static int returnInt(int i) {
32 | Log.i(TAG, "returnInt() called");
33 | return i;
34 | }
35 |
36 | public static long returnLong(long l) {
37 | Log.i(TAG, "returnLong() called");
38 | return l;
39 | }
40 |
41 | public static float returnFloat(float f) {
42 | Log.i(TAG, "returnFloat() called");
43 | return f;
44 | }
45 |
46 | public static double returnDouble(double d) {
47 | Log.i(TAG, "returnDouble() called");
48 | return d;
49 | }
50 |
51 | public static boolean returnBoolean(boolean b) {
52 | Log.i(TAG, "returnBoolean() called");
53 | return b;
54 | }
55 |
56 | public static String returnString(String s) {
57 | Log.i(TAG, "returnString() called with: s = [" + s + "]");
58 | return s;
59 | }
60 |
61 | public static String[] returnStringArray(String[] a) {
62 | Log.i(TAG, "returnStringArray() called with: a = [" + a + "]");
63 | return a;
64 | }
65 |
66 | public static ReturnTypeTarget returnCustom(ReturnTypeTarget r) {
67 | Log.i(TAG, "returnCustom() called with: r = [" + r + "]");
68 | return r;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/custom/Case17_SameMethod.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.custom;
2 |
3 | import android.util.Log;
4 |
5 | import de.robv.android.xposed.DexposedBridge;
6 | import de.robv.android.xposed.XC_MethodHook;
7 |
8 | /**
9 | * Created by weishu on 18/1/11.
10 | */
11 |
12 | public class Case17_SameMethod implements Case {
13 | private static final String TAG = "Case17_SameMethod";
14 |
15 | @Override
16 | public void hook() {
17 | DexposedBridge.findAndHookMethod(Target.class, "add", int.class, int.class, new XC_MethodHook() {
18 | @Override
19 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
20 | super.beforeHookedMethod(param);
21 | Log.d(TAG, "beforeHookedMethod() called with: param = [" + param + "]");
22 | }
23 |
24 | @Override
25 | protected void afterHookedMethod(MethodHookParam param) throws Throwable {
26 | super.afterHookedMethod(param);
27 | Log.d(TAG, "afterHookedMethod() called with: param = [" + param + "]");
28 | }
29 | });
30 |
31 | DexposedBridge.findAndHookMethod(Target.class, "add", int.class, int.class, new XC_MethodHook() {
32 | @Override
33 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
34 | super.beforeHookedMethod(param);
35 | Log.d(TAG, "beforeHookedMethod2() called with: param = [" + param + "]");
36 | }
37 |
38 | @Override
39 | protected void afterHookedMethod(MethodHookParam param) throws Throwable {
40 | super.afterHookedMethod(param);
41 | Log.d(TAG, "afterHookedMethod2() called with: param = [" + param + "]");
42 | }
43 | });
44 | }
45 |
46 | @Override
47 | public boolean validate(Object... args) {
48 | new Target().add(1, 2);
49 | new Target().add(3, 4);
50 | return false;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/custom/Case5.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.custom;
2 |
3 | import android.util.Log;
4 | import android.widget.TextView;
5 |
6 | import com.taobao.android.dexposed.utility.Unsafe;
7 |
8 | import java.lang.reflect.Method;
9 |
10 | import de.robv.android.xposed.DexposedBridge;
11 | import de.robv.android.xposed.XC_MethodHook;
12 | import me.weishu.epic.art.EpicNative;
13 |
14 | /**
15 | * Created by weishu on 17/11/6.
16 | */
17 |
18 | public class Case5 implements Case {
19 | @Override
20 | public void hook() {
21 | DexposedBridge.findAndHookMethod(TextView.class, "setPadding", int.class, int.class, int.class, int.class, new XC_MethodHook() {
22 | @Override
23 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
24 | super.beforeHookedMethod(param);
25 | if (param.thisObject != null) {
26 | Log.i("mylog", "this:" + Long.toHexString(Unsafe.getObjectAddress(param.thisObject)));
27 | }
28 | if (param.method != null) {
29 | Log.i("mylog", "mehod:" + Long.toHexString(EpicNative.getMethodAddress((Method) param.method)));
30 | }
31 | if (param.args != null) {
32 | for (Object arg : param.args) {
33 | Log.i("mylog", "param:" + arg);
34 | if (arg != null) {
35 | Log.i("mylog", "<" + arg.getClass() + "> : 0x" +
36 | Long.toHexString(Unsafe.getObjectAddress(arg)) + ", value: " + arg);
37 | } else {
38 | Log.i("mylog", "param: null");
39 | }
40 | }
41 | }
42 | }
43 |
44 | @Override
45 | protected void afterHookedMethod(MethodHookParam param) throws Throwable {
46 | super.afterHookedMethod(param);
47 | Log.i("mylog", "after");
48 | }
49 | });
50 | }
51 |
52 | @Override
53 | public boolean validate(Object... args) {
54 | return true;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/library/src/main/java/me/weishu/epic/art/arch/ShellCode.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017, weishu twsxtd@gmail.com
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package me.weishu.epic.art.arch;
18 |
19 | import java.nio.ByteBuffer;
20 | import java.nio.ByteOrder;
21 |
22 | public abstract class ShellCode {
23 |
24 | public abstract byte[] createDirectJump(long targetAddress);
25 |
26 | public abstract int sizeOfDirectJump();
27 |
28 | public abstract long toPC(long code);
29 |
30 | public abstract long toMem(long pc);
31 |
32 | public byte[] createCallOrigin(long originalAddress, byte[] originalPrologue) {
33 | byte[] callOriginal = new byte[sizeOfCallOrigin()];
34 | System.arraycopy(originalPrologue, 0, callOriginal, 0, sizeOfDirectJump());
35 | byte[] directJump = createDirectJump(toPC(originalAddress + sizeOfDirectJump()));
36 | System.arraycopy(directJump, 0, callOriginal, sizeOfDirectJump(), directJump.length);
37 | return callOriginal;
38 | }
39 |
40 | public int sizeOfCallOrigin() {
41 | return sizeOfDirectJump() * 2;
42 | }
43 |
44 | public abstract int sizeOfBridgeJump();
45 |
46 | public byte[] createBridgeJump(long targetAddress, long targetEntry, long srcAddress, long structAddress) {
47 | throw new RuntimeException("not impled");
48 | }
49 |
50 | static void writeInt(int i, ByteOrder order, byte[] target, int pos) {
51 | System.arraycopy(ByteBuffer.allocate(4).order(order).putInt(i).array(), 0, target, pos, 4);
52 | }
53 |
54 | static void writeLong(long i, ByteOrder order, byte[] target, int pos) {
55 | System.arraycopy(ByteBuffer.allocate(8).order(order).putLong(i).array(), 0, target, pos, 8);
56 | }
57 |
58 | public abstract String getName();
59 | }
60 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/custom/Case6.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.custom;
2 |
3 | import android.os.SystemClock;
4 | import android.util.Log;
5 |
6 | import com.taobao.android.dexposed.utility.Debug;
7 | import com.taobao.android.dexposed.utility.Unsafe;
8 |
9 | import java.lang.reflect.Method;
10 |
11 | import de.robv.android.xposed.XposedHelpers;
12 | import me.weishu.epic.art.EpicNative;
13 | import me.weishu.epic.art.method.ArtMethod;
14 |
15 | /**
16 | * Created by weishu on 17/11/6.
17 | */
18 |
19 | public class Case6 implements Case {
20 |
21 | private static final String TAG = "Case6";
22 |
23 | @Override
24 | public void hook() {
25 | Object test = new Object();
26 | Log.i(TAG, "test object:" + test);
27 |
28 | long testAddr = Unsafe.getObjectAddress(test);
29 | Log.i(TAG, "test object address :" + testAddr);
30 | Log.i(TAG, "test object :" + EpicNative.getObject(XposedHelpers.getLongField(Thread.currentThread(), "nativePeer"), testAddr));
31 |
32 | // Log.i(TAG, "object:" + EpicNative.getObject())
33 | final Method nanoTime = XposedHelpers.findMethodExact(System.class, "nanoTime");
34 | final Method uptimeMillis = XposedHelpers.findMethodExact(SystemClock.class, "uptimeMillis");
35 | final Method map = XposedHelpers.findMethodExact(Target.class, "test1", Object.class, int.class);
36 | final Method malloc = XposedHelpers.findMethodExact(Target.class, "test3", Object.class, int.class);
37 |
38 | ArtMethod artMethod1 = ArtMethod.of(nanoTime);
39 | ArtMethod artMethod2 = ArtMethod.of(uptimeMillis);
40 |
41 | ArtMethod artMethod3 = ArtMethod.of(map);
42 | ArtMethod artMethod4 = ArtMethod.of(malloc);
43 |
44 | Log.i(TAG, "nanoTime: addr: 0x" + artMethod1.getAddress() + ", entry:" + Debug.addrHex(artMethod1.getEntryPointFromQuickCompiledCode()));
45 | Log.i(TAG, "uptimeMills: addr: 0x" + artMethod2.getAddress() + ", entry:" + Debug.addrHex(artMethod2.getEntryPointFromQuickCompiledCode()));
46 | Log.i(TAG, "map : addr: 0x" + artMethod3.getAddress() + ", entry:" + Debug.addrHex(artMethod3.getEntryPointFromQuickCompiledCode()));
47 | Log.i(TAG, "malloc: addr: 0x" + artMethod4.getAddress() + ", entry:" + Debug.addrHex(artMethod4.getEntryPointFromQuickCompiledCode()));
48 | }
49 |
50 | @Override
51 | public boolean validate(Object... args) {
52 | return true;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/custom/Case1.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.custom;
2 |
3 | import android.os.SystemClock;
4 | import android.util.Log;
5 |
6 | import java.lang.reflect.Field;
7 |
8 | import de.robv.android.xposed.DexposedBridge;
9 | import de.robv.android.xposed.XC_MethodHook;
10 |
11 | /**
12 | * Created by weishu on 17/11/6.
13 | */
14 |
15 | public class Case1 implements Case {
16 |
17 | static Field sThread$target;
18 | static {
19 | try {
20 | // Thread#target
21 | sThread$target = Thread.class.getDeclaredField("target");
22 | sThread$target.setAccessible(true);
23 | } catch (Exception e) {
24 | e.printStackTrace();
25 | }
26 | }
27 |
28 | private static final String TAG = "Case1";
29 |
30 | @Override
31 | public void hook() {
32 | DexposedBridge.findAndHookMethod(Thread.class, "run", new XC_MethodHook() {
33 | @Override
34 | protected void afterHookedMethod(MethodHookParam param) throws Throwable {
35 | super.afterHookedMethod(param);
36 | Log.i(TAG, "afterHookedMethod: " + param.thisObject);
37 | Thread thread = (Thread) param.thisObject;
38 | Runnable target = (Runnable) sThread$target.get(thread);
39 | // start|threadName|priority|class|startTime|stacktrace
40 | Log.i(TAG, "runnable target:" + target);
41 | }
42 |
43 | @Override
44 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
45 | super.beforeHookedMethod(param);
46 | Log.i(TAG, "beforeHookedMethod: " + param.thisObject);
47 |
48 | }
49 | });
50 | }
51 |
52 | @Override
53 | public boolean validate(Object...args) {
54 | Thread t1 = new Thread(new Runnable() {
55 | @Override
56 | public void run() {
57 | Log.i(TAG, "before sleep");
58 | SystemClock.sleep(3000);
59 | Log.i(TAG, "after sleep");
60 | }
61 | });
62 | t1.start();
63 |
64 | class MyThread extends Thread {
65 | @Override
66 | public void run() {
67 | super.run();
68 | Log.i(TAG, "before sleep");
69 | SystemClock.sleep(3000);
70 | Log.i(TAG, "after sleep");
71 | }
72 | }
73 |
74 | Thread t2 = new MyThread();
75 | t2.start();
76 | return true;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/custom/Target.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.custom;
2 |
3 | import android.os.SystemClock;
4 | import android.util.Log;
5 |
6 | import java.lang.reflect.Method;
7 |
8 | import de.robv.android.xposed.XposedHelpers;
9 | import me.weishu.epic.art.method.ArtMethod;
10 |
11 | /**
12 | * Created by weishu on 17/11/3.
13 | */
14 |
15 | public class Target {
16 |
17 | public Target() {
18 | }
19 |
20 | public int test1(Object a, int b) {
21 | Log.i("mylog", "test1, arg1: " + a + " , arg2:" + b);
22 | new Runnable() {
23 |
24 | @Override
25 | public void run() {
26 | final Method enclosingMethod = getClass().getEnclosingMethod();
27 | long entry = ArtMethod.of(enclosingMethod).getEntryPointFromQuickCompiledCode();
28 | if (entry != ArtMethod.getQuickToInterpreterBridge()) {
29 | Log.w("mylog", "method compiled....");
30 | }
31 | Log.i("mylog", enclosingMethod + "entry: point: 0x" + Long.toHexString(entry));
32 | }
33 | }.run();
34 |
35 | return a.hashCode() + b;
36 | }
37 |
38 | public int returnConst(int a) {
39 | return a;
40 | }
41 |
42 | public int add(int a, int b) {
43 | return a + b;
44 | }
45 |
46 | public int plus(int a, int b) {
47 | return a + b;
48 | }
49 |
50 | public int test3(Object a, int b) {
51 | Log.i("mylog", "test1, arg1: " + a + " , arg2:" + b);
52 | return a.hashCode() + b;
53 | }
54 |
55 | public static int test4(int a) {
56 | return Integer.valueOf(a).hashCode();
57 | }
58 |
59 |
60 | public static float add(int a, float b) {
61 | return a + b;
62 | }
63 |
64 | public static int test2(Object a, int b) {
65 | Log.i("mylog", "test1, arg1: " + a + " , arg2:" + b);
66 |
67 | return a.hashCode() + b;
68 | }
69 |
70 | public long longRunMethod() {
71 | SystemClock.sleep(4000);
72 | return SystemClock.elapsedRealtime();
73 | }
74 |
75 | public static void validate() {
76 | final Method validate = XposedHelpers.findMethodExact(Target.class, "validate");
77 |
78 | long entry = ArtMethod.of(validate).getEntryPointFromQuickCompiledCode();
79 | if (entry != ArtMethod.getQuickToInterpreterBridge()) {
80 | Log.w("mylog", "method compiled....");
81 | new Target().test1("123", 1);
82 | }
83 | Log.i("mylog", validate + "entry: point: 0x" + Long.toHexString(entry));
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/library/src/main/java/com/taobao/android/dexposed/utility/Runtime.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Original work Copyright (c) 2016, Lody
3 | * Modified work Copyright (c) 2016, Alibaba Mobile Infrastructure (Android) Team
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.taobao.android.dexposed.utility;
19 |
20 | import android.util.Log;
21 |
22 | import java.lang.reflect.Method;
23 |
24 | import me.weishu.epic.art.method.ArtMethod;
25 |
26 | public class Runtime {
27 |
28 | private static final String TAG = "Runtime";
29 |
30 | private volatile static Boolean isThumb = null;
31 |
32 | private volatile static boolean g64 = false;
33 | private volatile static boolean isArt = true;
34 |
35 | static {
36 | try {
37 | g64 = (boolean) Class.forName("dalvik.system.VMRuntime").getDeclaredMethod("is64Bit").invoke(Class.forName("dalvik.system.VMRuntime").getDeclaredMethod("getRuntime").invoke(null));
38 | } catch (Exception e) {
39 | Log.e(TAG, "get is64Bit failed, default not 64bit!", e);
40 | g64 = false;
41 | }
42 | isArt = System.getProperty("java.vm.version").startsWith("2");
43 | Log.i(TAG, "is64Bit: " + g64 + ", isArt: " + isArt);
44 | }
45 |
46 | public static boolean is64Bit() {
47 | return g64;
48 | }
49 |
50 | public static boolean isArt() {
51 | return isArt;
52 | }
53 |
54 | public static boolean isThumb2() {
55 | if (isThumb != null) {
56 | return isThumb;
57 | }
58 |
59 | try {
60 | Method method = String.class.getDeclaredMethod("hashCode");
61 | ArtMethod artMethodStruct = ArtMethod.of(method);
62 | long entryPointFromQuickCompiledCode = artMethodStruct.getEntryPointFromQuickCompiledCode();
63 | Logger.w("Runtime", "isThumb2, entry: " + Long.toHexString(entryPointFromQuickCompiledCode));
64 | isThumb = ((entryPointFromQuickCompiledCode & 1) == 1);
65 | return isThumb;
66 | } catch (Throwable e) {
67 | Logger.w("Runtime", "isThumb2, error: " + e);
68 | return true; // Default Thumb2.
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/custom/Case12_MultiCallback.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.custom;
2 |
3 | import android.util.Log;
4 |
5 | import de.robv.android.xposed.DexposedBridge;
6 | import de.robv.android.xposed.XC_MethodHook;
7 |
8 | /**
9 | * Created by weishu on 17/11/21.
10 | */
11 | public class Case12_MultiCallback implements Case {
12 | private static final String TAG = "Case12_MultiCallback";
13 |
14 | int beforeCount = 0;
15 | int afterCount = 0;
16 |
17 | XC_MethodHook callback1 = new XC_MethodHook() {
18 | @Override
19 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
20 | super.beforeHookedMethod(param);
21 | Log.i(TAG, "beforeHookMethod 1");
22 | beforeCount++;
23 | }
24 |
25 | @Override
26 | protected void afterHookedMethod(MethodHookParam param) throws Throwable {
27 | super.afterHookedMethod(param);
28 | Log.i(TAG, "afterHookMethod 1");
29 | afterCount++;
30 | }
31 | };
32 |
33 | XC_MethodHook callback2 = new XC_MethodHook() {
34 | @Override
35 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
36 | super.beforeHookedMethod(param);
37 | Log.i(TAG, "beforeHookMethod 2 lalala ");
38 | beforeCount++;
39 | }
40 |
41 | @Override
42 | protected void afterHookedMethod(MethodHookParam param) throws Throwable {
43 | super.afterHookedMethod(param);
44 | Log.i(TAG, "afterHookMethod 2 lalala");
45 | afterCount++;
46 | }
47 | };
48 |
49 | XC_MethodHook callback3 = new XC_MethodHook() {
50 | @Override
51 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
52 | super.beforeHookedMethod(param);
53 | Log.i(TAG, "beforeHookMethod 3 zezeze");
54 | beforeCount++;
55 | }
56 |
57 | @Override
58 | protected void afterHookedMethod(MethodHookParam param) throws Throwable {
59 | super.afterHookedMethod(param);
60 | Log.i(TAG, "afterHookMethod 3 zezeze");
61 | afterCount++;
62 | }
63 | };
64 |
65 | @Override
66 | public void hook() {
67 | DexposedBridge.findAndHookMethod(Target.class, "test1", Object.class, int.class, callback1);
68 | DexposedBridge.findAndHookMethod(Target.class, "test1", Object.class, int.class, callback2);
69 | DexposedBridge.findAndHookMethod(Target.class, "test1", Object.class, int.class, callback3);
70 | }
71 |
72 | @Override
73 | public boolean validate(Object... args) {
74 | new Target().test1("123", 1);
75 | boolean ret = beforeCount == 3 && afterCount == 3;
76 | // reset.
77 | beforeCount = 0;
78 | afterCount = 0;
79 |
80 | return ret;
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/custom/Case16_SameEntry.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.custom;
2 |
3 | import android.util.Log;
4 |
5 | import com.taobao.android.dexposed.utility.Debug;
6 |
7 | import java.lang.reflect.Method;
8 |
9 | import de.robv.android.xposed.DexposedBridge;
10 | import de.robv.android.xposed.XC_MethodHook;
11 | import de.robv.android.xposed.XposedHelpers;
12 | import me.weishu.epic.art.method.ArtMethod;
13 |
14 | /**
15 | * Created by weishu on 18/1/8.
16 | */
17 |
18 | public class Case16_SameEntry implements Case {
19 | private static final String TAG = "Case16_SameEntry";
20 |
21 | @Override
22 | public void hook() {
23 | final Method add = XposedHelpers.findMethodExact(Target.class, "add", int.class, int.class);
24 | final Method plus = XposedHelpers.findMethodExact(Target.class, "plus", int.class, int.class);
25 |
26 | ArtMethod artMethod3 = ArtMethod.of(add);
27 | ArtMethod artMethod4 = ArtMethod.of(plus);
28 |
29 | Log.i(TAG, "plus: addr: " + Debug.addrHex(artMethod3.getAddress()) + ", entry:"
30 | + Debug.addrHex(artMethod3.getEntryPointFromQuickCompiledCode()));
31 | Log.i(TAG, "milus: addr: " + Debug.addrHex(artMethod4.getAddress()) + ", entry:"
32 | + Debug.addrHex(artMethod4.getEntryPointFromQuickCompiledCode()));
33 |
34 | DexposedBridge.hookMethod(add, new XC_MethodHook() {
35 | @Override
36 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
37 | super.beforeHookedMethod(param);
38 | Log.d(TAG, "beforeHookedMethod() called with: param = [" + param + "]");
39 | }
40 |
41 | @Override
42 | protected void afterHookedMethod(MethodHookParam param) throws Throwable {
43 | super.afterHookedMethod(param);
44 | Log.d(TAG, "afterHookedMethod() called with: param = [" + param + "]");
45 | }
46 | });
47 |
48 | DexposedBridge.hookMethod(plus, new XC_MethodHook() {
49 | @Override
50 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
51 | super.beforeHookedMethod(param);
52 | Log.d(TAG, "beforeHookedMethod() called with: param = [" + param + "]");
53 | }
54 |
55 | @Override
56 | protected void afterHookedMethod(MethodHookParam param) throws Throwable {
57 | super.afterHookedMethod(param);
58 | Log.d(TAG, "afterHookedMethod() called with: param = [" + param + "]");
59 | }
60 | });
61 | }
62 |
63 | @Override
64 | public boolean validate(Object... args) {
65 | Target target = new Target();
66 | int add = target.add(1, 2);
67 | Log.i(TAG, "1 + 2 = " + add);
68 | int plus = target.plus(3, 4);
69 | Log.i(TAG, "3 + 4 = " + plus);
70 | return true;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/library/src/main/java/com/taobao/android/dexposed/utility/NougatPolicy.java:
--------------------------------------------------------------------------------
1 | package com.taobao.android.dexposed.utility;
2 |
3 | import android.content.Context;
4 | import android.os.SystemClock;
5 | import android.util.Log;
6 |
7 | import java.lang.reflect.Method;
8 |
9 | public class NougatPolicy {
10 |
11 | private static class TraceLogger {
12 | static void i(String tag, String msg) {
13 | Log.i(tag, msg);
14 | }
15 | static void e(String tag, String msg) {
16 | Log.i(tag, msg);
17 | }
18 | static void e(String tag, String msg, Throwable e) {
19 | Log.i(tag, msg, e);
20 | }
21 | }
22 |
23 | private static final String TAG = "NougatPolicy";
24 |
25 | public static boolean fullCompile(Context context) {
26 | try {
27 | long t1 = SystemClock.elapsedRealtime();
28 | Object pm = getPackageManagerBinderProxy();
29 | if (pm == null) {
30 | TraceLogger.e(TAG, "can not found package service");
31 | return false;
32 | }
33 | /*
34 | @Override
35 | public boolean performDexOptMode(String packageName,
36 | boolean checkProfiles, String targetCompilerFilter, boolean force) {
37 | int dexOptStatus = performDexOptTraced(packageName, checkProfiles,
38 | targetCompilerFilter, force);
39 | return dexOptStatus != PackageDexOptimizer.DEX_OPT_FAILED;
40 | */
41 |
42 | final Method performDexOptMode = pm.getClass().getDeclaredMethod("performDexOptMode",
43 | String.class, boolean.class, String.class, boolean.class);
44 | boolean ret = (boolean) performDexOptMode.invoke(pm, context.getPackageName(), false, "speed", true);
45 | long cost = SystemClock.elapsedRealtime() - t1;
46 | Log.i(TAG, "full Compile cost: " + cost + " result:" + ret);
47 | return ret;
48 | } catch (Throwable e) {
49 | TraceLogger.e(TAG, "fullCompile failed:", e);
50 | return false;
51 | }
52 | }
53 |
54 | public static boolean clearCompileData(Context context) {
55 | boolean ret;
56 | try {
57 | Object pm = getPackageManagerBinderProxy();
58 | final Method performDexOpt = pm.getClass().getDeclaredMethod("performDexOpt", String.class,
59 | boolean.class, int.class, boolean.class);
60 | ret = (Boolean) performDexOpt.invoke(pm, context.getPackageName(), false, 2 /*install*/, true);
61 | } catch (Throwable e) {
62 | TraceLogger.e(TAG, "clear compile data failed", e);
63 | ret = false;
64 | }
65 | return ret;
66 | }
67 |
68 | private static Object getPackageManagerBinderProxy() throws Exception {
69 | Class> activityThread = Class.forName("android.app.ActivityThread");
70 | final Method getPackageManager = activityThread.getDeclaredMethod("getPackageManager");
71 | return getPackageManager.invoke(null);
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/custom/Case9_ThreadMonitor.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.custom;
2 |
3 | import android.util.Log;
4 |
5 | import java.util.concurrent.ExecutorService;
6 | import java.util.concurrent.Executors;
7 |
8 | import de.robv.android.xposed.DexposedBridge;
9 | import de.robv.android.xposed.XC_MethodHook;
10 |
11 | /**
12 | * Created by weishu on 17/11/10.
13 | */
14 | public class Case9_ThreadMonitor implements Case {
15 |
16 | private static final String TAG = "Case9_ThreadMonitor";
17 |
18 | @Override
19 | public void hook() {
20 | try {
21 |
22 | class ThreadMethodHook extends XC_MethodHook {
23 | @Override
24 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
25 | super.beforeHookedMethod(param);
26 | Thread t = (Thread) param.thisObject;
27 | Log.i(TAG, "thread:" + t + ", started..");
28 | }
29 |
30 | @Override
31 | protected void afterHookedMethod(MethodHookParam param) throws Throwable {
32 | super.afterHookedMethod(param);
33 | Thread t = (Thread) param.thisObject;
34 | Log.i(TAG, "thread:" + t + ", exit..");
35 | }
36 | }
37 |
38 | DexposedBridge.hookAllConstructors(Thread.class, new XC_MethodHook() {
39 | @Override
40 | protected void afterHookedMethod(MethodHookParam param) throws Throwable {
41 | super.afterHookedMethod(param);
42 | Thread thread = (Thread) param.thisObject;
43 | Class> clazz = thread.getClass();
44 | if (clazz != Thread.class) {
45 | Log.d(TAG, "found class extend Thread:" + clazz);
46 | DexposedBridge.findAndHookMethod(clazz, "run", new ThreadMethodHook());
47 | }
48 | Log.d(TAG, "Thread: " + thread.getName() + " class:" + thread.getClass() + " is created.");
49 | }
50 | });
51 | DexposedBridge.findAndHookMethod(Thread.class, "run", new ThreadMethodHook());
52 |
53 | } catch (Throwable e) {
54 | Log.e(TAG, "hook failed", e);
55 | }
56 | }
57 |
58 | @Override
59 | public boolean validate(Object... args) {
60 | new Thread(new Runnable() {
61 | @Override
62 | public void run() {
63 | Log.i(TAG, "I am started..");
64 | }
65 | }).start();
66 |
67 | new MyThread().start();
68 |
69 | final ExecutorService executorService = Executors.newCachedThreadPool();
70 | for (int i = 0; i < 4; i++) {
71 | // final int num = i;
72 | executorService.execute(new Runnable() {
73 | @Override
74 | public void run() {
75 | Log.i(TAG, " lalala");
76 | }
77 | });
78 | }
79 | return true;
80 | }
81 |
82 | static class MyThread extends Thread {
83 | @Override
84 | public void run() {
85 | Log.i(TAG, "dang dang dang..");
86 | }
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/library/src/main/cpp/art.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017, weishu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | #include "art.h"
17 | #include
18 | #include
19 |
20 | #define LOGV(...) ((void)__android_log_print(ANDROID_LOG_INFO, "epic.Native", __VA_ARGS__))
21 |
22 | #define ANDROID_R_API 30
23 | #define MAX_SEARCH_LEN 2000
24 |
25 | void* ArtHelper::runtime_instance_ = nullptr;
26 | int ArtHelper::api = 0;
27 |
28 | template
29 | inline int find_offset(void* hay, int size, T needle)
30 | {
31 | for (int i = 0; i < size; i += sizeof(T)) {
32 | auto current = reinterpret_cast(reinterpret_cast(hay) + i);
33 | if (*current == needle) {
34 | return i;
35 | }
36 | }
37 | return -1;
38 | }
39 |
40 | void ArtHelper::init(JNIEnv* env, int api)
41 | {
42 | ArtHelper::api = api;
43 | JavaVM* javaVM;
44 | env->GetJavaVM(&javaVM);
45 | JavaVMExt* javaVMExt = (JavaVMExt*)javaVM;
46 |
47 | void* runtime = javaVMExt->runtime;
48 | if (runtime == nullptr) {
49 | return;
50 | }
51 |
52 | if (api < ANDROID_R_API) {
53 | runtime_instance_ = runtime;
54 | } else {
55 | int vm_offset = find_offset(runtime, MAX_SEARCH_LEN, javaVM);
56 | runtime_instance_ = reinterpret_cast(reinterpret_cast(runtime) + vm_offset - offsetof(PartialRuntimeR, java_vm_));
57 | }
58 | }
59 |
60 | void* ArtHelper::getClassLinker()
61 | {
62 | if (runtime_instance_ == nullptr || api < ANDROID_R_API) {
63 | return nullptr;
64 | }
65 | PartialRuntimeR* runtimeR = (PartialRuntimeR*)runtime_instance_;
66 | return runtimeR->class_linker_;
67 | }
68 |
69 | void* ArtHelper::getJniIdManager()
70 | {
71 | if (runtime_instance_ == nullptr || api < ANDROID_R_API) {
72 | return nullptr;
73 | }
74 | PartialRuntimeR* runtimeR = (PartialRuntimeR*)runtime_instance_;
75 | return runtimeR->jni_id_manager_;
76 | }
77 |
78 | void* ArtHelper::getJitCodeCache()
79 | {
80 | if (runtime_instance_ == nullptr || api < ANDROID_R_API) {
81 | return nullptr;
82 | }
83 | PartialRuntimeR* runtimeR = (PartialRuntimeR*)runtime_instance_;
84 | return runtimeR->jit_code_cache_;
85 | }
86 |
87 | void* ArtHelper::getHeap()
88 | {
89 | if (runtime_instance_ == nullptr) {
90 | return nullptr;
91 | }
92 | if (api < 26) {
93 | Runtime_7X* runtime7X = (Runtime_7X*)runtime_instance_;
94 | return runtime7X->heap_;
95 | } else if (api < ANDROID_R_API) {
96 | Runtime_8X* runtime8X = (Runtime_8X*)runtime_instance_;
97 | LOGV("bootclasspath : %s", runtime8X->boot_class_path_string_.c_str());
98 | return runtime8X->heap_;
99 | } else {
100 | PartialRuntimeR* runtimeR = (PartialRuntimeR*)runtime_instance_;
101 | return runtimeR->heap_;
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/README_cn.md:
--------------------------------------------------------------------------------
1 | ## 简介
2 |
3 | Epic 是一个在虚拟机层面、以 Java Method 为粒度的 **运行时** AOP Hook 框架。简单来说,Epic 就是 ART 上的 [Dexposed](https://github.com/alibaba/dexposed)(支持 Android 5.0 ~ 11)。它可以拦截本进程内部几乎任意的 Java 方法调用,可用于实现 AOP 编程、运行时插桩、性能分析、安全审计等。
4 |
5 | Epic 被 [VirtualXposed](https://github.com/android-hacker/VirtualXposed) 以及 [太极](https://www.coolapk.com/apk/me.weishu.exp) 使用,用来实现非 Root 场景下的 Xposed 功能,已经经过了相当广泛的验证。
6 |
7 | 关于 Epic 的实现原理,可以参考 [本文](http://weishu.me/2017/11/23/dexposed-on-art/)。
8 |
9 | ## 使用
10 |
11 | ### 添加依赖
12 |
13 | 在你项目的 build.gradle 中添加如下依赖(jitpack 仓库):
14 |
15 | ```groovy
16 | dependencies {
17 | compile 'com.github.tiann:epic:0.11.2'
18 | }
19 | ```
20 |
21 | 然后就可以使用了。
22 |
23 |
24 | ### 几个例子
25 |
26 | 1. 监控 Java 线程的创建和销毁:
27 |
28 | ```java
29 | class ThreadMethodHook extends XC_MethodHook{
30 | @Override
31 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
32 | super.beforeHookedMethod(param);
33 | Thread t = (Thread) param.thisObject;
34 | Log.i(TAG, "thread:" + t + ", started..");
35 | }
36 |
37 | @Override
38 | protected void afterHookedMethod(MethodHookParam param) throws Throwable {
39 | super.afterHookedMethod(param);
40 | Thread t = (Thread) param.thisObject;
41 | Log.i(TAG, "thread:" + t + ", exit..");
42 | }
43 | }
44 |
45 | DexposedBridge.hookAllConstructors(Thread.class, new XC_MethodHook() {
46 | @Override
47 | protected void afterHookedMethod(MethodHookParam param) throws Throwable {
48 | super.afterHookedMethod(param);
49 | Thread thread = (Thread) param.thisObject;
50 | Class> clazz = thread.getClass();
51 | if (clazz != Thread.class) {
52 | Log.d(TAG, "found class extend Thread:" + clazz);
53 | DexposedBridge.findAndHookMethod(clazz, "run", new ThreadMethodHook());
54 | }
55 | Log.d(TAG, "Thread: " + thread.getName() + " class:" + thread.getClass() + " is created.");
56 | }
57 | });
58 | DexposedBridge.findAndHookMethod(Thread.class, "run", new ThreadMethodHook());
59 | ```
60 |
61 | 以上代码拦截了 `Thread` 类以及 `Thread` 类所有子类的 `run`方法,在 `run` 方法开始执行和退出的时候进行拦截,就可以知道进程内部所有 Java 线程创建和销毁的时机;更进一步,你可以结合 Systrace 等工具,来生成整个过程的执行流程图,比如:
62 |
63 |
64 |
65 | 2. 监控 dex 文件的加载:
66 |
67 | ```java
68 | DexposedBridge.findAndHookMethod(DexFile.class, "loadDex", String.class, String.class, int.class, new XC_MethodHook() {
69 | @Override
70 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
71 | super.beforeHookedMethod(param);
72 | String dex = (String) param.args[0];
73 | String odex = (String) param.args[1];
74 | Log.i(TAG, "load dex, input:" + dex + ", output:" + odex);
75 | }
76 | });
77 | ```
78 |
79 | ## 支持情况
80 |
81 | 目前 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。
82 |
83 |
84 | ## 已知问题
85 |
86 | 1. 受限于 inline hook 本身,短方法 (Thumb-2 下指令小于 8 个字节,ARM64 小于 16 字节) 无法支持。
87 | 2. 被完全内联的方法不支持。
88 |
89 | ## 致谢
90 |
91 | 1. [Dexposed](https://github.com/alibaba/dexposed)
92 | 2. [Xposed](http://repo.xposed.info/module/de.robv.android.xposed.installer)
93 | 3. [mar-v-in/ArtHook](https://github.com/mar-v-in/ArtHook)
94 | 4. [Nougat_dlfunctions](https://github.com/avs333/Nougat_dlfunctions.git)
95 |
96 |
97 | ## 联系我
98 |
99 | twsxtd@gmail.com
100 |
--------------------------------------------------------------------------------
/library/src/main/java/com/taobao/android/dexposed/utility/Platform.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Original work Copyright (c) 2016, Lody
3 | * Modified work Copyright (c) 2016, Alibaba Mobile Infrastructure (Android) Team
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.taobao.android.dexposed.utility;
19 |
20 | import java.nio.ByteBuffer;
21 | import java.nio.ByteOrder;
22 |
23 | public abstract class Platform {
24 |
25 | /*package*/ static Platform PLATFORM_INTERNAL;
26 |
27 | static {
28 | if (Runtime.is64Bit()) {
29 | PLATFORM_INTERNAL = new Platform64Bit();
30 | }else {
31 | PLATFORM_INTERNAL = new Platform32Bit();
32 | }
33 | }
34 |
35 | public static Platform getPlatform() {
36 | return PLATFORM_INTERNAL;
37 | }
38 |
39 | /**
40 | * Convert a byte array to int,
41 | * Use this function to get address from memory.
42 | *
43 | * @param data byte array
44 | * @return long
45 | */
46 | public abstract int orderByteToInt(byte[] data);
47 |
48 | /**
49 | * Convert a byte array to long,
50 | * Use this function to get address from memory.
51 | *
52 | * @param data byte array
53 | * @return long
54 | */
55 | public abstract long orderByteToLong(byte[] data);
56 |
57 | public abstract byte[] orderLongToByte(long serial, int length);
58 |
59 | public abstract byte[] orderIntToByte(int serial);
60 |
61 | public abstract int getIntSize();
62 |
63 |
64 | static class Platform32Bit extends Platform {
65 |
66 | @Override
67 | public int orderByteToInt(byte[] data) {
68 | return ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).getInt();
69 | }
70 |
71 | @Override
72 | public long orderByteToLong(byte[] data) {
73 | return ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).getInt() & 0xFFFFFFFFL;
74 | }
75 |
76 | @Override
77 | public byte[] orderLongToByte(long serial, int length) {
78 | return ByteBuffer.allocate(length).order(ByteOrder.LITTLE_ENDIAN).putInt((int) serial).array();
79 | }
80 |
81 | @Override
82 | public byte[] orderIntToByte(int serial) {
83 | return ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(serial).array();
84 | }
85 |
86 | @Override
87 | public int getIntSize() {
88 | return 4;
89 | }
90 |
91 |
92 | }
93 |
94 | static class Platform64Bit extends Platform {
95 |
96 | @Override
97 | public int orderByteToInt(byte[] data) {
98 | return ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).getInt();
99 | }
100 |
101 | @Override
102 | public long orderByteToLong(byte[] data) {
103 | return ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).getLong();
104 | }
105 |
106 | @Override
107 | public byte[] orderLongToByte(long serial, int length) {
108 | return ByteBuffer.allocate(length).order(ByteOrder.LITTLE_ENDIAN).putLong(serial).array();
109 | }
110 |
111 | @Override
112 | public byte[] orderIntToByte(int serial) {
113 | return ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(serial).array();
114 | }
115 |
116 | @Override
117 | public int getIntSize() {
118 | return 8;
119 | }
120 | }
121 |
122 | }
123 |
--------------------------------------------------------------------------------
/library/src/main/java/com/taobao/android/dexposed/utility/Debug.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Original work Copyright (c) 2014-2015, Marvin Wißfeld
3 | * Modified work Copyright (c) 2016, Alibaba Mobile Infrastructure (Android) Team
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.taobao.android.dexposed.utility;
19 |
20 | import android.util.Log;
21 |
22 | import java.io.BufferedReader;
23 | import java.io.FileReader;
24 | import java.io.IOException;
25 | import java.lang.reflect.Method;
26 |
27 | import me.weishu.epic.BuildConfig;
28 | import me.weishu.epic.art.method.ArtMethod;
29 |
30 | public final class Debug {
31 | private static final String TAG = "Dexposed";
32 |
33 | public static final boolean DEBUG = BuildConfig.DEBUG;
34 |
35 | private static final String RELASE_WRAN_STRING = "none in release mode.";
36 | private Debug() {
37 | }
38 |
39 | public static String addrHex(long i) {
40 | if (!DEBUG) {
41 | return RELASE_WRAN_STRING;
42 | }
43 |
44 | if (Runtime.is64Bit()) {
45 | return longHex(i);
46 | } else {
47 | return intHex((int) i);
48 | }
49 | }
50 |
51 | public static String longHex(long i) {
52 | return String.format("0x%016X", i);
53 | }
54 |
55 | public static String intHex(int i) {
56 | return String.format("0x%08X", i);
57 | }
58 |
59 | public static String byteHex(byte b) {
60 | return String.format("%02X", b);
61 | }
62 |
63 | public static String dump(byte[] b, long start) {
64 | StringBuffer sb = new StringBuffer();
65 | for (int i = 0; i < b.length; i++) {
66 | if (i % 8 == 0) {
67 | sb.append(addrHex(start + i)).append(":");
68 | }
69 | sb.append(byteHex(b[i])).append(" ");
70 | if (i % 8 == 7) {
71 | sb.append("\n");
72 | }
73 | }
74 | return sb.toString();
75 | }
76 | public static String hexdump(byte[] bytes, long start) {
77 | if (!DEBUG) {
78 | return RELASE_WRAN_STRING;
79 | }
80 | StringBuffer sb = new StringBuffer();
81 | for (int i = 0; i < bytes.length; i++) {
82 | if (i % 8 == 0) {
83 | sb.append(addrHex(start + i)).append(":");
84 | }
85 | sb.append(byteHex(bytes[i])).append(" ");
86 | if (i % 8 == 7) {
87 | sb.append("\n");
88 | }
89 | }
90 | return sb.toString();
91 | }
92 |
93 | public static String methodDescription(Method method) {
94 | return method.getDeclaringClass().getName() + "->" + method.getName() + " @" +
95 | addrHex(ArtMethod.of(method).getEntryPointFromQuickCompiledCode()) +
96 | " +" + addrHex(ArtMethod.of(method).getAddress());
97 | }
98 |
99 | public static void dumpMaps() {
100 | BufferedReader br = null;
101 | try {
102 | br = new BufferedReader(new FileReader("/proc/self/maps"));
103 | String line;
104 | while ((line = br.readLine()) != null) {
105 | Log.i(TAG, line);
106 | }
107 | } catch (IOException e) {
108 | Log.e(TAG, "dumpMaps error");
109 | } finally {
110 | if (br != null) {
111 | try {
112 | br.close();
113 | } catch (IOException e) {
114 | // ignore.
115 | }
116 | }
117 | }
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/library/src/main/java/me/weishu/epic/art/arch/Arm64.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017, weishu twsxtd@gmail.com
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package me.weishu.epic.art.arch;
18 |
19 | import java.nio.ByteOrder;
20 |
21 | public class Arm64 extends ShellCode {
22 |
23 | @Override
24 | public int sizeOfDirectJump() {
25 | return 4 * 4;
26 | }
27 |
28 | @Override
29 | public byte[] createDirectJump(long targetAddress) {
30 | byte[] instructions = new byte[]{
31 | 0x50, 0x00, 0x00, 0x58, // ldr x9, _targetAddress
32 | 0x00, 0x02, 0x1F, (byte) 0xD6, // br x9
33 | 0x00, 0x00, 0x00, 0x00, // targetAddress
34 | 0x00, 0x00, 0x00, 0x00 // targetAddress
35 | };
36 | writeLong(targetAddress, ByteOrder.LITTLE_ENDIAN, instructions, instructions.length - 8);
37 | return instructions;
38 | }
39 |
40 | @Override
41 | public byte[] createBridgeJump(long targetAddress, long targetEntry, long srcAddress, long structAddress) {
42 |
43 | byte[] instructions = new byte[]{
44 | 0x1f, 0x20, 0x03, (byte) 0xd5, // nop
45 | 0x69, 0x02, 0x00, 0x58, // ldr x9, source_method
46 | 0x1f, 0x00, 0x09, (byte) 0xeb, // cmp x0, x9
47 | (byte) 0xa1, 0x02, 0x00, 0x54, // bne 5f
48 | (byte) 0x80, 0x01, 0x00, 0x58, // ldr x0, target_method
49 |
50 | 0x29, 0x02, 0x00, 0x58, // ldr x9, struct
51 | (byte) 0xea, 0x03, 0x00, (byte) 0x91, // mov x10, sp
52 |
53 | 0x2a, 0x01, 0x00, (byte) 0xf9, // str x10, [x9, #0]
54 | 0x22, 0x05, 0x00, (byte) 0xf9, // str x2, [x9, #8]
55 |
56 | 0x23, 0x09, 0x00, (byte) 0xf9, // str x3, [x9, #16]
57 | (byte) 0xe3, 0x03, 0x09, (byte) 0xaa, // mov x3, x9
58 | 0x22, 0x01, 0x00, 0x58, // ldr x2, source_method
59 | 0x22, 0x0d, 0x00, (byte) 0xf9, // str x2, [x9, #24]
60 | (byte) 0xe2, 0x03, 0x13, (byte) 0xaa, // mov x2, x19
61 | (byte) 0x89, 0x00, 0x00, 0x58, // ldr x9, target_method_entry
62 | 0x20, 0x01, 0x1f, (byte) 0xd6, // br x9
63 |
64 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // target_method_address
65 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // target_method_entry
66 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // source_method
67 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // struct
68 |
69 | };
70 |
71 |
72 | writeLong(targetAddress, ByteOrder.LITTLE_ENDIAN, instructions,
73 | instructions.length - 32);
74 | writeLong(targetEntry, ByteOrder.LITTLE_ENDIAN, instructions,
75 | instructions.length - 24);
76 | writeLong(srcAddress,
77 | ByteOrder.LITTLE_ENDIAN, instructions, instructions.length - 16);
78 | writeLong(structAddress, ByteOrder.LITTLE_ENDIAN, instructions,
79 | instructions.length - 8);
80 |
81 | return instructions;
82 | }
83 |
84 | @Override
85 | public int sizeOfBridgeJump() {
86 | return 24 * 4;
87 | }
88 |
89 |
90 | @Override
91 | public long toPC(long code) {
92 | return code;
93 | }
94 |
95 | @Override
96 | public long toMem(long pc) {
97 | return pc;
98 | }
99 |
100 | @Override
101 | public String getName() {
102 | return "64-bit ARM";
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/arguments/AbsArgStaticCase.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests.arguments;
2 |
3 | import android.util.Log;
4 |
5 | import java.lang.reflect.Method;
6 | import java.util.Arrays;
7 | import java.util.Random;
8 |
9 | import de.robv.android.xposed.DexposedBridge;
10 | import de.robv.android.xposed.XposedHelpers;
11 | import me.weishu.epic.samples.tests.LogMethodHook;
12 | import me.weishu.epic.samples.tests.TestCase;
13 |
14 | /**
15 | * @author weishu
16 | * @date 17/11/14.
17 | */
18 |
19 | public abstract class AbsArgStaticCase extends TestCase {
20 |
21 | private final String TAG = getClass().getSimpleName();
22 |
23 | final long[] args;
24 |
25 | private Random r = new Random();
26 |
27 | public AbsArgStaticCase() {
28 | super(null);
29 | args = new long[8];
30 | for (int i = 0; i < args.length; i++) {
31 | args[i] = 0L;
32 | }
33 |
34 | name = getClass().getSimpleName();
35 | }
36 |
37 | @Override
38 | public void test() {
39 | DexposedBridge.hookMethod(getTargetMethod(), new LogMethodHook() {
40 | @Override
41 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
42 | super.beforeHookedMethod(param);
43 | int length = param.args.length;
44 | if (length > 0) {
45 | for (int i = 0; i < length; i++) {
46 | long tmp = ((Number) param.args[i]).longValue();
47 | args[i] = tmp;
48 | }
49 | }
50 | }
51 | });
52 | }
53 |
54 | @Override
55 | public boolean predicate() {
56 | long[] arguments = getArguments();
57 | Log.i(TAG, "call arguments: " + Arrays.toString(toHex(arguments)));
58 |
59 | makeCall(arguments);
60 |
61 | boolean ret = true;
62 |
63 | int length = getArgumentNumber();
64 | for (int i = 0; i < length; i++) {
65 | if (arguments[i] != args[i]) {
66 | ret = false;
67 | Log.i(TAG, "hooked arguments: " + Arrays.toString(toHex(args)));
68 | }
69 | }
70 | return ret;
71 | }
72 |
73 | private long[] getArguments() {
74 | final int argumentNumber = getArgumentNumber();
75 | long[] arguments = new long[argumentNumber];
76 | for (int i = 0; i < arguments.length; i++) {
77 | arguments[i] = nextLong();
78 | }
79 | return arguments;
80 | }
81 |
82 | protected Method getTargetMethod() {
83 | final int argumentNumber = getArgumentNumber();
84 | String methodName = "arg" + argumentNumber;
85 |
86 | Method method = XposedHelpers.findMethodExact(ArgumentTarget.class, methodName, getParamsSignature());
87 | Log.i(TAG, "find target Method:" + method);
88 | return method;
89 | }
90 |
91 | protected int getArgumentNumber() {
92 | return getParamsSignature().length;
93 | }
94 |
95 | protected void makeCall(long... args) {
96 | Log.i(TAG, getName() + " make call with arguments:" + Arrays.toString(args));
97 | }
98 |
99 | private long nextLong() {
100 | int ret = r.nextInt();
101 | return ret;
102 | }
103 |
104 | private Class>[] getParamsSignature() {
105 | String className = getClass().getSimpleName();
106 | String signature = className.substring("ArgStatic".length());
107 | final int length = signature.length();
108 | Class>[] clazz = new Class>[length];
109 | for (int i = 0; i < length; i++) {
110 | final char c = signature.charAt(i);
111 | if (c == '4') {
112 | clazz[i] = int.class;
113 | } else if (c == '8') {
114 | clazz[i] = long.class;
115 | } else {
116 | throw new RuntimeException("Unknown signature!!");
117 | }
118 | }
119 | return clazz;
120 | }
121 |
122 | private String[] toHex(long[] value) {
123 | String[] ret = new String[value.length];
124 |
125 | for (int i = 0; i < value.length; i++) {
126 | ret[i] = Long.toHexString(value[i]);
127 | }
128 | return ret;
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/library/src/main/java/me/weishu/epic/art/arch/Arm64_2.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017, weishu twsxtd@gmail.com
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package me.weishu.epic.art.arch;
18 |
19 | import java.nio.ByteOrder;
20 |
21 | public class Arm64_2 extends ShellCode {
22 |
23 | @Override
24 | public int sizeOfDirectJump() {
25 | return 4 * 4;
26 | }
27 |
28 | @Override
29 | public byte[] createDirectJump(long targetAddress) {
30 | byte[] instructions = new byte[]{
31 | 0x50, 0x00, 0x00, 0x58, // ldr x16, _targetAddress
32 | 0x00, 0x02, 0x1F, (byte) 0xD6, // br x16
33 | 0x00, 0x00, 0x00, 0x00, // targetAddress
34 | 0x00, 0x00, 0x00, 0x00 // targetAddress
35 | };
36 | writeLong(targetAddress, ByteOrder.LITTLE_ENDIAN, instructions, instructions.length - 8);
37 | return instructions;
38 | }
39 |
40 | @Override
41 | public byte[] createBridgeJump(long targetAddress, long targetEntry, long srcAddress, long structAddress) {
42 |
43 | byte[] instructions = new byte[] {
44 |
45 | /*
46 | ldr x17, 3f
47 | cmp x0, x17
48 | bne 5f
49 | ldr x0, 1f
50 | ldr x17, 4f
51 | mov x16, sp
52 | str x16, [x17, #0]
53 | str x2, [x17, #8]
54 | ldr x16, 3f
55 | str x16, [x17, #16]
56 | mov x2, x17
57 | ldr x17, 2f
58 | br x17
59 |
60 | 1:
61 | .quad 0x0
62 | 2:
63 | .quad 0x0
64 | 3:
65 | .quad 0x0
66 | 4:
67 | .quad 0x0
68 |
69 | 5:
70 | mov x0, x17
71 |
72 | */
73 | 0x1f, 0x20, 0x03, (byte) 0xd5, // nop
74 | 0x31, 0x02, 0x00, 0x58,
75 | 0x1f, 0x00, 0x11, (byte)0xeb,
76 | (byte)0x61, 0x02, 0x00, 0x54,
77 | (byte)0x40, 0x01, 0x00, 0x58,
78 | (byte)0xf1, 0x01, 0x00, 0x58,
79 | (byte)0xf0, 0x03, 0x00, (byte)0x91,
80 | (byte)0x30, 0x02, 0x00, (byte)0xf9,
81 | 0x22, 0x06, 0x00, (byte)0xf9,
82 | 0x30, 0x01, 0x00, (byte)0x58,
83 | (byte)0x30, 0x0a, 0x00, (byte)0xf9,
84 | (byte)0xe2, 0x03, 0x11, (byte)0xaa,
85 | (byte)0x91, 0x00, 0x00, 0x58,
86 | (byte)0x20, 0x02, 0x1f, (byte)0xd6,
87 |
88 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // target_method_address
89 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // target_method_entry
90 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // source_method
91 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // struct
92 | };
93 |
94 | writeLong(targetAddress, ByteOrder.LITTLE_ENDIAN, instructions,
95 | instructions.length - 32);
96 | writeLong(targetEntry, ByteOrder.LITTLE_ENDIAN, instructions,
97 | instructions.length - 24);
98 | writeLong(srcAddress,
99 | ByteOrder.LITTLE_ENDIAN, instructions, instructions.length - 16);
100 | writeLong(structAddress, ByteOrder.LITTLE_ENDIAN, instructions,
101 | instructions.length - 8);
102 |
103 | return instructions;
104 | }
105 |
106 | @Override
107 | public int sizeOfBridgeJump() {
108 | return 22 * 4;
109 | }
110 |
111 |
112 | @Override
113 | public long toPC(long code) {
114 | return code;
115 | }
116 |
117 | @Override
118 | public long toMem(long pc) {
119 | return pc;
120 | }
121 |
122 | @Override
123 | public String getName() {
124 | return "64-bit ARM(Android M)";
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/MainActivity.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 | import android.util.Log;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.BaseExpandableListAdapter;
9 | import android.widget.Button;
10 | import android.widget.ExpandableListAdapter;
11 | import android.widget.ExpandableListView;
12 | import android.widget.ImageView;
13 | import android.widget.TextView;
14 |
15 | import java.util.List;
16 |
17 | import me.weishu.epic.samples.tests.TestCase;
18 | import me.weishu.epic.samples.tests.TestManager;
19 | import me.weishu.epic.samples.tests.TestSuite;
20 |
21 | public class MainActivity extends Activity {
22 |
23 | private static final String TAG = "MainActivity";
24 |
25 | ExpandableListView listView;
26 |
27 | List allSuites;
28 |
29 | @Override
30 | protected void onCreate(Bundle savedInstanceState) {
31 | super.onCreate(savedInstanceState);
32 | Log.i(TAG, "savedInstance:" + savedInstanceState);
33 | setContentView(R.layout.activity_main);
34 | setTitle("Epic Test");
35 | listView = findViewById(R.id.list);
36 | allSuites = TestManager.getInstance().getAllSuites();
37 | ExpandableListAdapter adapter = new MyAdapter();
38 | listView.setAdapter(adapter);
39 |
40 | }
41 |
42 | private class MyAdapter extends BaseExpandableListAdapter {
43 |
44 | @Override
45 | public int getGroupCount() {
46 | return allSuites.size();
47 | }
48 |
49 | @Override
50 | public int getChildrenCount(int groupPosition) {
51 | return allSuites.get(groupPosition).getAllCases().size();
52 | }
53 |
54 | @Override
55 | public TestSuite getGroup(int groupPosition) {
56 | return allSuites.get(groupPosition);
57 | }
58 |
59 | @Override
60 | public TestCase getChild(int groupPosition, int childPosition) {
61 | return allSuites.get(groupPosition).getAllCases().get(childPosition);
62 | }
63 |
64 | @Override
65 | public long getGroupId(int groupPosition) {
66 | return 0;
67 | }
68 |
69 | @Override
70 | public long getChildId(int groupPosition, int childPosition) {
71 | return 0;
72 | }
73 |
74 | @Override
75 | public boolean hasStableIds() {
76 | return false;
77 | }
78 |
79 | @Override
80 | public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
81 | View parentView = View.inflate(parent.getContext(), R.layout.parent_layout, null);
82 | TextView t = parentView.findViewById(R.id.text);
83 | t.setText(getGroup(groupPosition).getName());
84 |
85 | ImageView indicator = parentView.findViewById(R.id.indicator);
86 | if (isExpanded) {
87 | indicator.setImageResource(R.drawable.arrow_down);
88 | } else {
89 | indicator.setImageResource(R.drawable.arrow_up);
90 | }
91 | return parentView;
92 | }
93 |
94 | @Override
95 | public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
96 | final TestCase child = getChild(groupPosition, childPosition);
97 |
98 | final View childView = View.inflate(parent.getContext(), R.layout.child_layout, null);
99 | final TextView title = childView.findViewById(R.id.label);
100 | title.setText(child.getName());
101 |
102 | Button test = childView.findViewById(R.id.test);
103 | test.setOnClickListener(new View.OnClickListener() {
104 | @Override
105 | public void onClick(View v) {
106 | child.test();
107 | }
108 | });
109 |
110 | Button validate = childView.findViewById(R.id.validate);
111 | validate.setOnClickListener(new View.OnClickListener() {
112 | @Override
113 | public void onClick(View v) {
114 | boolean validate = child.validate();
115 | final int color = v.getContext().getResources().getColor(validate ?
116 | android.R.color.holo_green_light : android.R.color.holo_red_light);
117 | childView.setBackgroundColor(color);
118 | }
119 | });
120 |
121 | return childView;
122 | }
123 |
124 | @Override
125 | public boolean isChildSelectable(int groupPosition, int childPosition) {
126 | return false;
127 | }
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [ ](https://bintray.com/twsxtd/maven/epic/_latestVersion)
2 | [](https://gitter.im/android-hacker/epic?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
3 |
4 | [中文文档入口](README_cn.md "中文")
5 |
6 | What is it?
7 | -----------
8 |
9 | Epic is the continuation of [Dexposed](https://github.com/alibaba/dexposed) on ART (Supports 5.0 ~ 11).
10 |
11 | > Dexposed is a powerful yet non-invasive runtime [AOP (Aspect-oriented Programming)](http://en.wikipedia.org/wiki/Aspect-oriented_programming) framework
12 | for Android app development, based on the work of open-source [Xposed](https://github.com/rovo89/Xposed) [framework](https://github.com/rovo89/XposedBridge) project.
13 | >
14 | > The AOP of Dexposed is implemented purely non-invasive, without any annotation processor,
15 | weaver or bytecode rewriter. The integration is as simple as loading a small JNI library
16 | in just one line of code at the initialization phase of your app.
17 | >
18 | > Not only the code of your app, but also the code of Android framework that running in your
19 | app process can be hooked.
20 |
21 | Epic keeps the same API and all capability of Dexposed, you can do anything which is supported by Dexposed.
22 |
23 | Typical use-cases
24 | -----------------
25 |
26 | * Classic AOP programming
27 | * Instrumentation (for testing, performance monitoring and etc.)
28 | * Security audit (sensitive api check,Smash shell)
29 | * Just for fun :)
30 |
31 |
32 | Integration
33 | -----------
34 |
35 | Directly add epic aar to your project as compile libraries, Gradle dependency like following(jitpack):
36 |
37 | ```groovy
38 | dependencies {
39 | compile 'com.github.tiann:epic:0.11.2'
40 | }
41 | ```
42 |
43 | Everything is ready.
44 |
45 | Basic usage
46 | -----------
47 |
48 | There are three injection points for a given method: *before*, *after*, *origin*.
49 |
50 | Example 1: monitor the creation and destroy of java thread
51 |
52 | ```java
53 | class ThreadMethodHook extends XC_MethodHook{
54 | @Override
55 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
56 | super.beforeHookedMethod(param);
57 | Thread t = (Thread) param.thisObject;
58 | Log.i(TAG, "thread:" + t + ", started..");
59 | }
60 |
61 | @Override
62 | protected void afterHookedMethod(MethodHookParam param) throws Throwable {
63 | super.afterHookedMethod(param);
64 | Thread t = (Thread) param.thisObject;
65 | Log.i(TAG, "thread:" + t + ", exit..");
66 | }
67 | }
68 |
69 | DexposedBridge.hookAllConstructors(Thread.class, new XC_MethodHook() {
70 | @Override
71 | protected void afterHookedMethod(MethodHookParam param) throws Throwable {
72 | super.afterHookedMethod(param);
73 | Thread thread = (Thread) param.thisObject;
74 | Class> clazz = thread.getClass();
75 | if (clazz != Thread.class) {
76 | Log.d(TAG, "found class extend Thread:" + clazz);
77 | DexposedBridge.findAndHookMethod(clazz, "run", new ThreadMethodHook());
78 | }
79 | Log.d(TAG, "Thread: " + thread.getName() + " class:" + thread.getClass() + " is created.");
80 | }
81 | });
82 | DexposedBridge.findAndHookMethod(Thread.class, "run", new ThreadMethodHook());
83 | ```
84 |
85 | Example 2: Intercept the dex loading behavior
86 |
87 | ```java
88 | DexposedBridge.findAndHookMethod(DexFile.class, "loadDex", String.class, String.class, int.class, new XC_MethodHook() {
89 | @Override
90 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
91 | super.beforeHookedMethod(param);
92 | String dex = (String) param.args[0];
93 | String odex = (String) param.args[1];
94 | Log.i(TAG, "load dex, input:" + dex + ", output:" + odex);
95 | }
96 | });
97 | ```
98 |
99 | Checkout the `sample` project to find out more.
100 |
101 | Support
102 | ----------
103 |
104 | 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).
105 |
106 |
107 | Known Issues
108 | -------------
109 |
110 | 1. Short method (instruction less 8 bytes on thumb2 or less 16bytes in ARM64) are not supported.
111 | 2. Fully inline methods are not supported.
112 |
113 | Contribute
114 | ----------
115 |
116 | We are open to constructive contributions from the community, especially pull request
117 | 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.**
118 |
119 | 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 :)
120 |
121 | Thanks
122 | -------
123 |
124 | 1. [Dexposed](https://github.com/alibaba/dexposed)
125 | 2. [Xposed](http://repo.xposed.info/module/de.robv.android.xposed.installer)
126 | 3. [mar-v-in/ArtHook](https://github.com/mar-v-in/ArtHook)
127 | 4. [Nougat_dlfunctions](https://github.com/avs333/Nougat_dlfunctions.git)
128 |
129 | Contact me
130 | ----------
131 |
132 | twsxtd@gmail.com
133 |
134 | [Join discussion](https://gitter.im/android-hacker/epic?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
135 |
--------------------------------------------------------------------------------
/library/src/main/cpp/art.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017, weishu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | #ifndef EPIC_ART_H
18 | #define EPIC_ART_H
19 |
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 |
26 | // Android 6.0: http://androidxref.com/6.0.0_r5/xref/art/runtime/runtime.h
27 | // Android 7.0: http://androidxref.com/7.0.0_r1/xref/art/runtime/runtime.h
28 | // Android 7.1.1: http://androidxref.com/7.1.1_r6/xref/art/runtime/runtime.h
29 | // Android 8.0: http://androidxref.com/8.0.0_r4/xref/art/runtime/runtime.h
30 |
31 | struct Runtime_7X {
32 |
33 | uint64_t callee_save_methods_[3];
34 | void* pre_allocated_OutOfMemoryError_;
35 | void* pre_allocated_NoClassDefFoundError_;
36 | void* resolution_method_;
37 | void* imt_conflict_method_;
38 | // Unresolved method has the same behavior as the conflict method, it is used by the class linker
39 | // for differentiating between unfilled imt slots vs conflict slots in superclasses.
40 | void* imt_unimplemented_method_;
41 | void* sentinel_;
42 |
43 | int instruction_set_;
44 | uint32_t callee_save_method_frame_infos_[9]; // QuickMethodFrameInfo = uint32_t * 3
45 |
46 | void* compiler_callbacks_;
47 | bool is_zygote_;
48 | bool must_relocate_;
49 | bool is_concurrent_gc_enabled_;
50 | bool is_explicit_gc_disabled_;
51 | bool dex2oat_enabled_;
52 | bool image_dex2oat_enabled_;
53 |
54 | std::string compiler_executable_;
55 | std::string patchoat_executable_;
56 | std::vector compiler_options_;
57 | std::vector image_compiler_options_;
58 | std::string image_location_;
59 |
60 | std::string boot_class_path_string_;
61 | std::string class_path_string_;
62 | std::vector properties_;
63 |
64 | // The default stack size for managed threads created by the runtime.
65 | size_t default_stack_size_;
66 |
67 | void* heap_;
68 | };
69 |
70 | struct Runtime_8X {
71 |
72 | uint64_t callee_save_methods_[3];
73 | void* pre_allocated_OutOfMemoryError_;
74 | void* pre_allocated_NoClassDefFoundError_;
75 | void* resolution_method_;
76 | void* imt_conflict_method_;
77 | // Unresolved method has the same behavior as the conflict method, it is used by the class linker
78 | // for differentiating between unfilled imt slots vs conflict slots in superclasses.
79 | void* imt_unimplemented_method_;
80 | void* sentinel_;
81 |
82 | int instruction_set_;
83 | uint32_t callee_save_method_frame_infos_[9]; // QuickMethodFrameInfo = uint32_t * 3
84 |
85 | void* compiler_callbacks_;
86 | bool is_zygote_;
87 | bool must_relocate_;
88 | bool is_concurrent_gc_enabled_;
89 | bool is_explicit_gc_disabled_;
90 | bool dex2oat_enabled_;
91 | bool image_dex2oat_enabled_;
92 |
93 | std::string compiler_executable_;
94 | std::string patchoat_executable_;
95 | std::vector compiler_options_;
96 | std::vector image_compiler_options_;
97 | std::string image_location_;
98 |
99 | std::string boot_class_path_string_;
100 | std::string class_path_string_;
101 | std::vector properties_;
102 |
103 | std::list agents_;
104 | std::vector plugins_;
105 |
106 | // The default stack size for managed threads created by the runtime.
107 | size_t default_stack_size_;
108 |
109 | void* heap_;
110 | };
111 |
112 | struct PartialRuntimeR {
113 | void* heap_;
114 |
115 | void* jit_arena_pool_;
116 | void* arena_pool_;
117 | // Special low 4gb pool for compiler linear alloc. We need ArtFields to be in low 4gb if we are
118 | // compiling using a 32 bit image on a 64 bit compiler in case we resolve things in the image
119 | // since the field arrays are int arrays in this case.
120 | void* low_4gb_arena_pool_;
121 |
122 | // Shared linear alloc for now.
123 | void* linear_alloc_;
124 |
125 | // The number of spins that are done before thread suspension is used to forcibly inflate.
126 | size_t max_spins_before_thin_lock_inflation_;
127 | void* monitor_list_;
128 | void* monitor_pool_;
129 |
130 | void* thread_list_;
131 |
132 | void* intern_table_;
133 |
134 | void* class_linker_;
135 |
136 | void* signal_catcher_;
137 |
138 | void* jni_id_manager_;
139 |
140 | void* java_vm_;
141 |
142 | void* jit_;
143 | void* jit_code_cache_;
144 | };
145 |
146 | struct JavaVMExt {
147 | void* functions;
148 | void* runtime;
149 | };
150 |
151 | class ArtHelper {
152 | public:
153 | static void init(JNIEnv*, int);
154 | static void* getRuntimeInstance() { return runtime_instance_; }
155 | static void* getClassLinker();
156 | static void* getJniIdManager();
157 | static void* getJitCodeCache();
158 | static void* getHeap();
159 |
160 | private:
161 | static void* runtime_instance_;
162 | static int api;
163 | };
164 |
165 | #endif //EPIC_ART_H
166 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/library/src/main/java/com/taobao/android/dexposed/DeviceCheck.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Original work Copyright (c) 2005-2008, The Android Open Source Project
3 | * Modified work Copyright (c) 2013, rovo89 and Tungstwenty
4 | * Modified work Copyright (c) 2015, Alibaba Mobile Infrastructure (Android) Team
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package com.taobao.android.dexposed;
20 |
21 | import android.annotation.SuppressLint;
22 | import android.content.Context;
23 | import android.util.Log;
24 |
25 | import java.io.BufferedReader;
26 | import java.io.InputStreamReader;
27 | import java.lang.reflect.InvocationTargetException;
28 | import java.lang.reflect.Method;
29 |
30 | public class DeviceCheck {
31 |
32 | private static final String SELECT_RUNTIME_PROPERTY = "persist.sys.dalvik.vm.lib";
33 | private static final String LIB_DALVIK = "libdvm.so";
34 | private static final String LIB_ART = "libart.so";
35 | private static final String LIB_ART_D = "libartd.so";
36 |
37 | private static boolean isCheckedDeviceSupport = false;
38 | private static boolean isDeviceSupportable = false;
39 |
40 | private static boolean isDalvikMode() {
41 | String vmMode = getCurrentRuntimeValue();
42 | if("Dalvik".equals(vmMode)){
43 | return true;
44 | }
45 | return false;
46 | }
47 |
48 | private static String getCurrentRuntimeValue() {
49 | try {
50 | Class> systemProperties = Class
51 | .forName("android.os.SystemProperties");
52 | try {
53 | Method get = systemProperties.getMethod("get", String.class,
54 | String.class);
55 | if (get == null) {
56 | return "WTF?!";
57 | }
58 | try {
59 | final String value = (String) get.invoke(systemProperties,
60 | SELECT_RUNTIME_PROPERTY,
61 | /* Assuming default is */"Dalvik");
62 | if (LIB_DALVIK.equals(value)) {
63 | return "Dalvik";
64 | } else if (LIB_ART.equals(value)) {
65 | return "ART";
66 | } else if (LIB_ART_D.equals(value)) {
67 | return "ART debug build";
68 | }
69 |
70 | return value;
71 | } catch (IllegalAccessException e) {
72 | return "IllegalAccessException";
73 | } catch (IllegalArgumentException e) {
74 | return "IllegalArgumentException";
75 | } catch (InvocationTargetException e) {
76 | return "InvocationTargetException";
77 | }
78 | } catch (NoSuchMethodException e) {
79 | return "SystemProperties.get(String key, String def) method is not found";
80 | }
81 | } catch (ClassNotFoundException e) {
82 | return "SystemProperties class is not found";
83 | }
84 | }
85 |
86 | private static boolean isSupportSDKVersion() {
87 | if (android.os.Build.VERSION.SDK_INT >= 14 && android.os.Build.VERSION.SDK_INT < 20) {
88 | return true;
89 | } else if(android.os.Build.VERSION.SDK_INT == 10 || android.os.Build.VERSION.SDK_INT == 9){
90 | return true;
91 | }
92 | return false;
93 | }
94 |
95 | private static boolean isX86CPU() {
96 | Process process = null;
97 | String abi = null;
98 | InputStreamReader ir = null;
99 | BufferedReader input = null;
100 | try {
101 | process = Runtime.getRuntime().exec("getprop ro.product.cpu.abi");
102 | ir = new InputStreamReader(process.getInputStream());
103 | input = new BufferedReader(ir);
104 | abi = input.readLine();
105 | if (abi.contains("x86")) {
106 | return true;
107 | }
108 | } catch (Exception e) {
109 | } finally {
110 | if (input != null) {
111 | try {
112 | input.close();
113 | } catch (Exception e) {
114 | }
115 | }
116 |
117 | if (ir != null) {
118 | try {
119 | ir.close();
120 | } catch (Exception e) {
121 | }
122 | }
123 |
124 | if (process != null) {
125 | try {
126 | process.destroy();
127 | } catch (Exception e) {
128 | }
129 | }
130 | }
131 |
132 | return false;
133 | }
134 |
135 | public static synchronized boolean isDeviceSupport(Context context) {
136 | // return memory checked value.
137 | try {
138 | if (isCheckedDeviceSupport)
139 | return isDeviceSupportable;
140 |
141 | if (!isX86CPU() && !isYunOS()) {
142 | isDeviceSupportable = true;
143 | } else {
144 | isDeviceSupportable = false;
145 | }
146 | } finally {
147 | Log.d("hotpatch", "device support is " + isDeviceSupportable + "checked" + isCheckedDeviceSupport);
148 | isCheckedDeviceSupport = true;
149 | }
150 | return isDeviceSupportable;
151 | }
152 |
153 | @SuppressLint("DefaultLocale")
154 | public static boolean isYunOS() {
155 | String s1 = null;
156 | String s2 = null;
157 | try {
158 | Method m = Class.forName("android.os.SystemProperties").getMethod(
159 | "get", String.class);
160 | s1 = (String) m.invoke(null, "ro.yunos.version");
161 | s2 = (String) m.invoke(null, "java.vm.name");
162 | } catch (NoSuchMethodException a) {
163 | } catch (ClassNotFoundException b) {
164 | } catch (IllegalAccessException c) {
165 | } catch (InvocationTargetException d) {
166 | }
167 | if ((s2 != null && s2.toLowerCase().contains("lemur")) || (s1 != null && s1.trim().length() > 0)) {
168 | return true;
169 | } else {
170 | return false;
171 | }
172 | }
173 | }
174 |
--------------------------------------------------------------------------------
/library/src/main/java/com/taobao/android/dexposed/utility/Unsafe.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2014-2015 Marvin Wißfeld
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.taobao.android.dexposed.utility;
18 |
19 | import android.util.Log;
20 |
21 | import java.lang.reflect.Field;
22 |
23 | public final class Unsafe {
24 | private static final String TAG = "Unsafe";
25 |
26 | private static Object unsafe;
27 | private static Class unsafeClass;
28 |
29 | static {
30 | try {
31 | unsafeClass = Class.forName("sun.misc.Unsafe");
32 | Field theUnsafe = unsafeClass.getDeclaredField("theUnsafe");
33 | theUnsafe.setAccessible(true);
34 | unsafe = theUnsafe.get(null);
35 | } catch (Exception e) {
36 | try {
37 | final Field theUnsafe = unsafeClass.getDeclaredField("THE_ONE");
38 | theUnsafe.setAccessible(true);
39 | unsafe = theUnsafe.get(null);
40 | } catch (Exception e2) {
41 | Log.w(TAG, "Unsafe not found o.O");
42 | }
43 | }
44 | }
45 |
46 | private Unsafe() {
47 | }
48 |
49 | @SuppressWarnings("unchecked")
50 | public static int arrayBaseOffset(Class cls) {
51 | try {
52 | return (int) unsafeClass.getDeclaredMethod("arrayBaseOffset", Class.class).invoke(unsafe, cls);
53 | } catch (Exception e) {
54 | Log.w(TAG, e);
55 | return 0;
56 | }
57 | }
58 |
59 | @SuppressWarnings("unchecked")
60 | public static int arrayIndexScale(Class cls) {
61 | try {
62 | return (int) unsafeClass.getDeclaredMethod("arrayIndexScale", Class.class).invoke(unsafe, cls);
63 | } catch (Exception e) {
64 | Log.w(TAG, e);
65 | return 0;
66 | }
67 | }
68 |
69 | @SuppressWarnings("unchecked")
70 | public static long objectFieldOffset(Field field) {
71 | try {
72 | return (long) unsafeClass.getDeclaredMethod("objectFieldOffset", Field.class).invoke(unsafe, field);
73 | } catch (Exception e) {
74 | Log.w(TAG, e);
75 | return 0;
76 | }
77 | }
78 |
79 | @SuppressWarnings("unchecked")
80 | public static int getInt(Object array, long offset) {
81 | try {
82 | return (int) unsafeClass.getDeclaredMethod("getInt", Object.class, long.class).invoke(unsafe, array, offset);
83 | } catch (Exception e) {
84 | Log.w(TAG, e);
85 | return 0;
86 | }
87 | }
88 |
89 | @SuppressWarnings("unchecked")
90 | public static long getLong(Object array, long offset) {
91 | try {
92 | return (long) unsafeClass.getDeclaredMethod("getLong", Object.class, long.class).invoke(unsafe, array, offset);
93 | } catch (Exception e) {
94 | Log.w(TAG, e);
95 | return 0;
96 | }
97 | }
98 |
99 | @SuppressWarnings("unchecked")
100 | public static void putLong(Object array, long offset, long value) {
101 | try {
102 | unsafeClass.getDeclaredMethod("putLongVolatile", Object.class, long.class, long.class).invoke(unsafe, array, offset, value);
103 | } catch (Exception e) {
104 | try {
105 | unsafeClass.getDeclaredMethod("putLong", Object.class, long.class, long.class).invoke(unsafe, array, offset, value);
106 | } catch (Exception e1) {
107 | Log.w(TAG, e);
108 | }
109 | }
110 | }
111 |
112 | @SuppressWarnings("unchecked")
113 | public static void putInt(Object array, long offset, int value) {
114 | try {
115 | unsafeClass.getDeclaredMethod("putIntVolatile", Object.class, long.class, int.class).invoke(unsafe, array, offset, value);
116 | } catch (Exception e) {
117 | try {
118 | unsafeClass.getDeclaredMethod("putIntVolatile", Object.class, long.class, int.class).invoke(unsafe, array, offset, value);
119 | } catch (Exception e1) {
120 | Log.w(TAG, e);
121 | }
122 | }
123 | }
124 |
125 | public static long getObjectAddress(Object obj) {
126 | try {
127 | Object[] array = new Object[]{obj};
128 | if (arrayIndexScale(Object[].class) == 8) {
129 | return getLong(array, arrayBaseOffset(Object[].class));
130 | } else {
131 | return 0xffffffffL & getInt(array, arrayBaseOffset(Object[].class));
132 | }
133 | } catch (Exception e) {
134 | Log.w(TAG, e);
135 | return -1;
136 | }
137 | }
138 |
139 | /**
140 | * get Object from address, refer: http://mishadoff.com/blog/java-magic-part-4-sun-dot-misc-dot-unsafe/
141 | * @param address the address of a object.
142 | * @return
143 | */
144 | public static Object getObject(long address) {
145 | Object[] array = new Object[]{null};
146 | long baseOffset = arrayBaseOffset(Object[].class);
147 | if (Runtime.is64Bit()) {
148 | putLong(array, baseOffset, address);
149 | } else {
150 | putInt(array, baseOffset, (int) address);
151 | }
152 | return array[0];
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/library/src/main/java/me/weishu/epic/art/arch/Thumb2.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017, weishu twsxtd@gmail.com
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package me.weishu.epic.art.arch;
18 |
19 | import java.nio.ByteOrder;
20 |
21 |
22 | public class Thumb2 extends ShellCode {
23 |
24 | @Override
25 | public int sizeOfDirectJump() {
26 | return 12;
27 | }
28 |
29 | @Override
30 | public byte[] createDirectJump(long targetAddress) {
31 | byte[] instructions = new byte[] {
32 | (byte) 0xdf, (byte) 0xf8, 0x00, (byte) 0xf0, // ldr pc, [pc]
33 | 0, 0, 0, 0
34 | };
35 | writeInt((int) targetAddress, ByteOrder.LITTLE_ENDIAN, instructions,
36 | instructions.length - 4);
37 | return instructions;
38 | }
39 |
40 | @Override
41 | public byte[] createBridgeJump(long targetAddress, long targetEntry, long srcAddress, long structAddress) {
42 | // 有问题,参数丢失。
43 | // byte[] instructions = new byte[] {
44 | // (byte) 0xdf, (byte) 0xf8, 0x18, (byte) 0xc0, // ldr ip, [pc, #24], ip = source_method_address
45 | //
46 | // (byte) 0x01, (byte) 0x91, // str r1, [sp, #4]
47 | // (byte) 0x02, (byte) 0x92, // str r2, [sp, #8]
48 | //
49 | // (byte) 0x03, (byte) 0x93, // str r3, [sp, #12]
50 | // (byte) 0x62, (byte) 0x46, // mov r2, ip
51 | //
52 | // 0x01, 0x48, // ldr r0, [pc, #4]
53 | // 0x6b, 0x46, // mov r3, sp
54 | //
55 | // (byte) 0xdf, (byte) 0xf8, 0x04, (byte) 0xf0, // ldr pc, [pc, #4]
56 | //
57 | // 0x0, 0x0, 0x0, 0x0, // target_method_pos_x
58 | // 0x0, 0x0, 0x0, 0x0, // target_method_entry_point
59 | // 0x0, 0x0, 0x0, 0x0, // src_method_pos_x
60 | // };
61 | // writeInt((int) targetAddress, ByteOrder.LITTLE_ENDIAN, instructions,
62 | // instructions.length - 12);
63 | // writeInt((int) targetEntry,
64 | // ByteOrder.LITTLE_ENDIAN, instructions, instructions.length - 8);
65 | // writeInt((int) srcAddress, ByteOrder.LITTLE_ENDIAN, instructions,
66 | // instructions.length - 4);
67 |
68 | byte[] instructions = new byte[]{
69 |
70 | (byte) 0xdf, (byte) 0xf8, (byte) 0x30, (byte) 0xc0, // ldr ip, [pc, #48] ip = source method address
71 |
72 | (byte) 0x60, (byte) 0x45, // cmp r0, ip if r0 != ip
73 | (byte) 0x40, (byte) 0xf0, (byte) 0x19, (byte) 0x80, // bne.w 1f jump label 1:
74 | (byte) 0x08, (byte) 0x48, // ldr r0, [pc, #28] r0 = target_method_address
75 | (byte) 0xdf, (byte) 0xf8, (byte) 0x28, (byte) 0xc0, // ldr ip, [pc, #38] ip = struct address
76 | (byte) 0xcc, (byte) 0xf8, (byte) 0x00, (byte) 0xd0, // str sp, [ip, #0]
77 | (byte) 0xcc, (byte) 0xf8, (byte) 0x04, (byte) 0x20, // str r2, [ip, #4]
78 | (byte) 0xcc, (byte) 0xf8, (byte) 0x08, (byte) 0x30, // str r3, [ip, #8]
79 |
80 | (byte) 0x63, (byte) 0x46, // mov r3, ip
81 | (byte) 0x05, (byte) 0x4a, // ldr r2, [pc, #16] r2 = source_method_address
82 | (byte) 0xcc, (byte) 0xf8, (byte) 0x0c, (byte) 0x20, // str r2, [ip, #12]
83 | (byte) 0x4a, (byte) 0x46, // move r2, r9
84 | (byte) 0x4a, (byte) 0x46, // move r2, r9
85 |
86 | (byte) 0xdf, (byte) 0xf8, (byte) 0x04, (byte) 0xf0, // ldr pc, [pc, #4]
87 |
88 | 0x0, 0x0, 0x0, 0x0, // target_method_pos_x
89 | 0x0, 0x0, 0x0, 0x0, // target_method_entry_point
90 | 0x0, 0x0, 0x0, 0x0, // src_method_address
91 | 0x0, 0x0, 0x0, 0x0, // struct address (sp, r1, r2)
92 | // 1:
93 | };
94 |
95 | writeInt((int) targetAddress, ByteOrder.LITTLE_ENDIAN, instructions,
96 | instructions.length - 16);
97 | writeInt((int) targetEntry, ByteOrder.LITTLE_ENDIAN, instructions,
98 | instructions.length - 12);
99 | writeInt((int) srcAddress,
100 | ByteOrder.LITTLE_ENDIAN, instructions, instructions.length - 8);
101 | writeInt((int) structAddress, ByteOrder.LITTLE_ENDIAN, instructions,
102 | instructions.length - 4);
103 |
104 | return instructions;
105 | }
106 |
107 | @Override
108 | public int sizeOfBridgeJump() {
109 | return 15 * 4;
110 | }
111 |
112 | @Override
113 | public long toPC(long code) {
114 | return toMem(code) + 1;
115 | }
116 |
117 | @Override
118 | public long toMem(long pc) {
119 | return pc & ~0x1;
120 | }
121 |
122 | @Override
123 | public String getName() {
124 | return "Thumb2";
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/library/src/main/java/me/weishu/epic/art/EpicNative.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017, weishu twsxtd@gmail.com
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package me.weishu.epic.art;
18 |
19 | import android.util.Log;
20 |
21 | import com.taobao.android.dexposed.DeviceCheck;
22 | import com.taobao.android.dexposed.utility.Debug;
23 | import com.taobao.android.dexposed.utility.Logger;
24 | import com.taobao.android.dexposed.utility.Unsafe;
25 |
26 | import java.lang.reflect.Member;
27 |
28 | import de.robv.android.xposed.XposedHelpers;
29 |
30 | import static com.taobao.android.dexposed.utility.Debug.addrHex;
31 |
32 |
33 | public final class EpicNative {
34 |
35 | private static final String TAG = "EpicNative";
36 | private static volatile boolean useUnsafe = false;
37 | static {
38 | try {
39 | System.loadLibrary("epic");
40 | useUnsafe = DeviceCheck.isYunOS() || !isGetObjectAvailable();
41 | Log.i(TAG, "use unsafe ? " + useUnsafe);
42 | } catch (Throwable e) {
43 | Log.e(TAG, "init EpicNative error", e);
44 | }
45 | }
46 |
47 | public static native long mmap(int length);
48 |
49 | public static native boolean munmap(long address, int length);
50 |
51 | public static native void memcpy(long src, long dest, int length);
52 |
53 | public static native void memput(byte[] bytes, long dest);
54 |
55 | public static native byte[] memget(long src, int length);
56 |
57 | public static native boolean munprotect(long addr, long len);
58 |
59 | public static native long getMethodAddress(Member method);
60 |
61 | public static native void MakeInitializedClassVisibilyInitialized(long self);
62 |
63 | public static native boolean cacheflush(long addr, long len);
64 |
65 | public static native long malloc(int sizeOfPtr);
66 |
67 | public static native Object getObjectNative(long self, long address);
68 |
69 | private static native boolean isGetObjectAvailable();
70 |
71 | public static Object getObject(long self, long address) {
72 | if (useUnsafe) {
73 | return Unsafe.getObject(address);
74 | } else {
75 | return getObjectNative(self, address);
76 | }
77 | }
78 |
79 | public static native boolean compileMethod(Member method, long self);
80 |
81 | /**
82 | * suspend all running thread momently
83 | * @return a handle to resume all thread, used by {@link #resumeAll(long)}
84 | */
85 | public static native long suspendAll();
86 |
87 | /**
88 | * resume all thread which are suspend by {@link #suspendAll()}
89 | * only work abobe Android N
90 | * @param cookie the cookie return by {@link #suspendAll()}
91 | */
92 | public static native void resumeAll(long cookie);
93 |
94 | /**
95 | * stop jit compiler in runtime.
96 | * Warning: Just for experiment Do not call this now!!!
97 | * @return cookie use by {@link #startJit(long)}
98 | */
99 | public static native long stopJit();
100 |
101 | /**
102 | * start jit compiler stop by {@link #stopJit()}
103 | * Warning: Just for experiment Do not call this now!!!
104 | * @param cookie the cookie return by {@link #stopJit()}
105 | */
106 | public static native void startJit(long cookie);
107 |
108 | // FIXME: 17/12/29 reimplement it with pure native code.
109 | static native boolean activateNative(long jumpToAddress, long pc, long sizeOfTargetJump, long sizeOfBridgeJump, byte[] code);
110 |
111 | /**
112 | * Disable the moving gc of runtime.
113 | * Warning: Just for experiment Do not call this now!!!
114 | * @param api the api level
115 | */
116 | public static native void disableMovingGc(int api);
117 |
118 |
119 | private EpicNative() {
120 | }
121 |
122 | public static boolean compileMethod(Member method) {
123 | final long nativePeer = XposedHelpers.getLongField(Thread.currentThread(), "nativePeer");
124 | return compileMethod(method, nativePeer);
125 | }
126 |
127 | public static Object getObject(long address) {
128 | final long nativePeer = XposedHelpers.getLongField(Thread.currentThread(), "nativePeer");
129 | return getObject(nativePeer, address);
130 | }
131 |
132 | public static void MakeInitializedClassVisibilyInitialized() {
133 | final long nativePeer = XposedHelpers.getLongField(Thread.currentThread(), "nativePeer");
134 | MakeInitializedClassVisibilyInitialized(nativePeer);
135 | }
136 |
137 | public static long map(int length) {
138 | long m = mmap(length);
139 | Logger.i(TAG, "Mapped memory of size " + length + " at " + addrHex(m));
140 | return m;
141 | }
142 |
143 | public static boolean unmap(long address, int length) {
144 | Logger.d(TAG, "Removing mapped memory of size " + length + " at " + addrHex(address));
145 | return munmap(address, length);
146 | }
147 |
148 | public static void put(byte[] bytes, long dest) {
149 | if (Debug.DEBUG) {
150 | Logger.d(TAG, "Writing memory to: " + addrHex(dest));
151 | Logger.d(TAG, Debug.hexdump(bytes, dest));
152 | }
153 | memput(bytes, dest);
154 | }
155 |
156 | public static byte[] get(long src, int length) {
157 | Logger.d(TAG, "Reading " + length + " bytes from: " + addrHex(src));
158 | byte[] bytes = memget(src, length);
159 | Logger.d(TAG, Debug.hexdump(bytes, src));
160 | return bytes;
161 | }
162 |
163 | public static boolean unprotect(long addr, long len) {
164 | Logger.d(TAG, "Disabling mprotect from " + addrHex(addr));
165 | return munprotect(addr, len);
166 | }
167 |
168 | public static void copy(long src, long dst, int length) {
169 | Logger.d(TAG, "Copy " + length + " bytes form " + addrHex(src) + " to " + addrHex(dst));
170 | memcpy(src, dst, length);
171 | }
172 |
173 | }
174 |
175 |
176 |
177 |
--------------------------------------------------------------------------------
/library/src/main/java/me/weishu/epic/art/Trampoline.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017, weishu twsxtd@gmail.com
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package me.weishu.epic.art;
18 |
19 | import com.taobao.android.dexposed.utility.Debug;
20 | import com.taobao.android.dexposed.utility.Logger;
21 | import com.taobao.android.dexposed.utility.Runtime;
22 |
23 | import java.lang.reflect.Method;
24 | import java.util.HashSet;
25 | import java.util.Set;
26 |
27 | import me.weishu.epic.art.arch.ShellCode;
28 | import me.weishu.epic.art.entry.Entry;
29 | import me.weishu.epic.art.entry.Entry64;
30 | import me.weishu.epic.art.method.ArtMethod;
31 |
32 | class Trampoline {
33 | private static final String TAG = "Trampoline";
34 |
35 | private final ShellCode shellCode;
36 | private final long jumpToAddress;
37 | private final byte[] originalCode;
38 | private int trampolineSize;
39 | private long trampolineAddress;
40 | private boolean active;
41 |
42 | // private ArtMethod artOrigin;
43 | private Set segments = new HashSet<>();
44 |
45 | Trampoline(ShellCode shellCode, long entryPoint) {
46 | this.shellCode = shellCode;
47 | this.jumpToAddress = shellCode.toMem(entryPoint);
48 | this.originalCode = EpicNative.get(jumpToAddress, shellCode.sizeOfDirectJump());
49 | }
50 |
51 | public boolean install(ArtMethod originMethod){
52 | boolean modified = segments.add(originMethod);
53 | if (!modified) {
54 | // Already hooked, ignore
55 | Logger.d(TAG, originMethod + " is already hooked, return.");
56 | return true;
57 | }
58 |
59 | byte[] page = create();
60 | EpicNative.put(page, getTrampolineAddress());
61 |
62 | int quickCompiledCodeSize = Epic.getQuickCompiledCodeSize(originMethod);
63 | int sizeOfDirectJump = shellCode.sizeOfDirectJump();
64 | if (quickCompiledCodeSize < sizeOfDirectJump) {
65 | Logger.w(TAG, originMethod.toGenericString() + " quickCompiledCodeSize: " + quickCompiledCodeSize);
66 | originMethod.setEntryPointFromQuickCompiledCode(getTrampolinePc());
67 | return true;
68 | }
69 | // 这里是绝对不能改EntryPoint的,碰到GC就挂(GC暂停线程的时候,遍历所有线程堆栈,如果被hook的方法在堆栈上,那就GG)
70 | // source.setEntryPointFromQuickCompiledCode(script.getTrampolinePc());
71 | return activate();
72 | }
73 |
74 | private long getTrampolineAddress() {
75 | if (getSize() != trampolineSize) {
76 | alloc();
77 | }
78 | return trampolineAddress;
79 | }
80 |
81 | private long getTrampolinePc() {
82 | return shellCode.toPC(getTrampolineAddress());
83 | }
84 |
85 | private void alloc() {
86 | if (trampolineAddress != 0) {
87 | free();
88 | }
89 | trampolineSize = getSize();
90 | trampolineAddress = EpicNative.map(trampolineSize);
91 | Logger.d(TAG, "Trampoline alloc:" + trampolineSize + ", addr: 0x" + Long.toHexString(trampolineAddress));
92 | }
93 |
94 | private void free() {
95 | if (trampolineAddress != 0) {
96 | EpicNative.unmap(trampolineAddress, trampolineSize);
97 | trampolineAddress = 0;
98 | trampolineSize = 0;
99 | }
100 |
101 | if (active) {
102 | EpicNative.put(originalCode, jumpToAddress);
103 | }
104 | }
105 |
106 | private int getSize() {
107 | int count = 0;
108 | count += shellCode.sizeOfBridgeJump() * segments.size();
109 | count += shellCode.sizeOfCallOrigin();
110 | return count;
111 | }
112 |
113 | private byte[] create() {
114 | Logger.d(TAG, "create trampoline." + segments);
115 | byte[] mainPage = new byte[getSize()];
116 |
117 | int offset = 0;
118 | for (ArtMethod method : segments) {
119 | byte[] bridgeJump = createTrampoline(method);
120 | int length = bridgeJump.length;
121 | System.arraycopy(bridgeJump, 0, mainPage, offset, length);
122 | offset += length;
123 | }
124 |
125 | byte[] callOriginal = shellCode.createCallOrigin(jumpToAddress, originalCode);
126 | System.arraycopy(callOriginal, 0, mainPage, offset, callOriginal.length);
127 |
128 | return mainPage;
129 | }
130 |
131 | private boolean activate() {
132 | long pc = getTrampolinePc();
133 | Logger.d(TAG, "Writing direct jump entry " + Debug.addrHex(pc) + " to origin entry: 0x" + Debug.addrHex(jumpToAddress));
134 | synchronized (Trampoline.class) {
135 | return EpicNative.activateNative(jumpToAddress, pc, shellCode.sizeOfDirectJump(),
136 | shellCode.sizeOfBridgeJump(), shellCode.createDirectJump(pc));
137 | }
138 | }
139 |
140 | @Override
141 | protected void finalize() throws Throwable {
142 | free();
143 | super.finalize();
144 | }
145 |
146 | private byte[] createTrampoline(ArtMethod source){
147 | final Epic.MethodInfo methodInfo = Epic.getMethodInfo(source.getAddress());
148 | final Class> returnType = methodInfo.returnType;
149 |
150 | // Method bridgeMethod = Runtime.is64Bit() ? (Build.VERSION.SDK_INT == 23 ? Entry64_2.getBridgeMethod(methodInfo) : Entry64.getBridgeMethod(returnType))
151 | // : Entry.getBridgeMethod(returnType);
152 | Method bridgeMethod = Runtime.is64Bit() ? Entry64.getBridgeMethod(returnType)
153 | : Entry.getBridgeMethod(returnType);
154 |
155 | final ArtMethod target = ArtMethod.of(bridgeMethod);
156 | long targetAddress = target.getAddress();
157 | long targetEntry = target.getEntryPointFromQuickCompiledCode();
158 | long sourceAddress = source.getAddress();
159 | long structAddress = EpicNative.malloc(4);
160 |
161 | Logger.d(TAG, "targetAddress:"+ Debug.longHex(targetAddress));
162 | Logger.d(TAG, "sourceAddress:"+ Debug.longHex(sourceAddress));
163 | Logger.d(TAG, "targetEntry:"+ Debug.longHex(targetEntry));
164 | Logger.d(TAG, "structAddress:"+ Debug.longHex(structAddress));
165 |
166 | return shellCode.createBridgeJump(targetAddress, targetEntry, sourceAddress, structAddress);
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/app/src/main/java/me/weishu/epic/samples/tests/TestManager.java:
--------------------------------------------------------------------------------
1 | package me.weishu.epic.samples.tests;
2 |
3 | import android.os.Build;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 | import java.util.Set;
8 |
9 | import me.weishu.epic.samples.tests.arguments.ArgStatic0;
10 | import me.weishu.epic.samples.tests.arguments.ArgStatic4;
11 | import me.weishu.epic.samples.tests.arguments.ArgStatic44;
12 | import me.weishu.epic.samples.tests.arguments.ArgStatic444;
13 | import me.weishu.epic.samples.tests.arguments.ArgStatic4444;
14 | import me.weishu.epic.samples.tests.arguments.ArgStatic4448;
15 | import me.weishu.epic.samples.tests.arguments.ArgStatic448;
16 | import me.weishu.epic.samples.tests.arguments.ArgStatic4484;
17 | import me.weishu.epic.samples.tests.arguments.ArgStatic48;
18 | import me.weishu.epic.samples.tests.arguments.ArgStatic484;
19 | import me.weishu.epic.samples.tests.arguments.ArgStatic4844;
20 | import me.weishu.epic.samples.tests.arguments.ArgStatic488;
21 | import me.weishu.epic.samples.tests.arguments.ArgStatic4888;
22 | import me.weishu.epic.samples.tests.arguments.ArgStatic8;
23 | import me.weishu.epic.samples.tests.arguments.ArgStatic84;
24 | import me.weishu.epic.samples.tests.arguments.ArgStatic844;
25 | import me.weishu.epic.samples.tests.arguments.ArgStatic8444;
26 | import me.weishu.epic.samples.tests.arguments.ArgStatic8448;
27 | import me.weishu.epic.samples.tests.arguments.ArgStatic848;
28 | import me.weishu.epic.samples.tests.arguments.ArgStatic8484;
29 | import me.weishu.epic.samples.tests.arguments.ArgStatic8488;
30 | import me.weishu.epic.samples.tests.arguments.ArgStatic88;
31 | import me.weishu.epic.samples.tests.arguments.ArgStatic884;
32 | import me.weishu.epic.samples.tests.arguments.ArgStatic8844;
33 | import me.weishu.epic.samples.tests.arguments.ArgStatic8848;
34 | import me.weishu.epic.samples.tests.arguments.ArgStatic888;
35 | import me.weishu.epic.samples.tests.arguments.ArgStatic8884;
36 | import me.weishu.epic.samples.tests.arguments.ArgStatic8888;
37 | import me.weishu.epic.samples.tests.custom.Case1;
38 | import me.weishu.epic.samples.tests.custom.Case10_Default_Constructor;
39 | import me.weishu.epic.samples.tests.custom.Case11_SuspendAll;
40 | import me.weishu.epic.samples.tests.custom.Case12_MultiCallback;
41 | import me.weishu.epic.samples.tests.custom.Case13_FastNative;
42 | import me.weishu.epic.samples.tests.custom.Case14_GC;
43 | import me.weishu.epic.samples.tests.custom.Case17_SameMethod;
44 | import me.weishu.epic.samples.tests.custom.Case18_returnConst;
45 | import me.weishu.epic.samples.tests.custom.Case2;
46 | import me.weishu.epic.samples.tests.custom.Case3;
47 | import me.weishu.epic.samples.tests.custom.Case4;
48 | import me.weishu.epic.samples.tests.custom.Case5;
49 | import me.weishu.epic.samples.tests.custom.Case6;
50 | import me.weishu.epic.samples.tests.custom.Case7;
51 | import me.weishu.epic.samples.tests.custom.Case8_Activity_onCreate;
52 | import me.weishu.epic.samples.tests.custom.Case9_ThreadMonitor;
53 | import me.weishu.epic.samples.tests.custom.CaseManager;
54 | import me.weishu.epic.samples.tests.invoketype.InvokeConstructor;
55 | import me.weishu.epic.samples.tests.returntype.BooleanType;
56 | import me.weishu.epic.samples.tests.returntype.ByteType;
57 | import me.weishu.epic.samples.tests.returntype.CharType;
58 | import me.weishu.epic.samples.tests.returntype.CustomType;
59 | import me.weishu.epic.samples.tests.returntype.DoubleType;
60 | import me.weishu.epic.samples.tests.returntype.FloatType;
61 | import me.weishu.epic.samples.tests.returntype.IntType;
62 | import me.weishu.epic.samples.tests.returntype.LongType;
63 | import me.weishu.epic.samples.tests.returntype.ShortType;
64 | import me.weishu.epic.samples.tests.returntype.StringArrayType;
65 | import me.weishu.epic.samples.tests.returntype.StringType;
66 | import me.weishu.epic.samples.tests.returntype.VoidType;
67 |
68 | /**
69 | * Created by weishu on 17/11/13.
70 | */
71 |
72 | public class TestManager {
73 | private static TestManager sManager = new TestManager();
74 |
75 | private List suites = new ArrayList<>();
76 |
77 | private TestManager() {
78 | initAllSuites();
79 | }
80 |
81 | public static TestManager getInstance() {
82 | return sManager;
83 | }
84 |
85 | public void addSuite(TestSuite suite) {
86 | suites.add(suite);
87 | }
88 |
89 | public List getAllSuites() {
90 | return suites;
91 | }
92 |
93 | private void initAllSuites() {
94 | TestSuite returnType = new TestSuite("返回值测试");
95 |
96 | returnType.addCase(new VoidType());
97 | returnType.addCase(new ByteType());
98 | returnType.addCase(new ShortType());
99 | returnType.addCase(new CharType());
100 | returnType.addCase(new IntType());
101 | returnType.addCase(new FloatType());
102 | returnType.addCase(new LongType());
103 | returnType.addCase(new DoubleType());
104 | returnType.addCase(new BooleanType());
105 | returnType.addCase(new StringType());
106 | returnType.addCase(new StringArrayType());
107 | returnType.addCase(new CustomType());
108 |
109 | addSuite(returnType);
110 |
111 |
112 | TestSuite arguments = new TestSuite("参数测试");
113 |
114 | arguments.addCase(new ArgStatic0());
115 | arguments.addCase(new ArgStatic4());
116 | arguments.addCase(new ArgStatic8());
117 |
118 | arguments.addCase(new ArgStatic44());
119 | arguments.addCase(new ArgStatic48());
120 | arguments.addCase(new ArgStatic84());
121 | arguments.addCase(new ArgStatic88());
122 |
123 | arguments.addCase(new ArgStatic444());
124 | arguments.addCase(new ArgStatic448());
125 | arguments.addCase(new ArgStatic484());
126 | arguments.addCase(new ArgStatic844());
127 | arguments.addCase(new ArgStatic488());
128 | arguments.addCase(new ArgStatic848());
129 | arguments.addCase(new ArgStatic884());
130 | arguments.addCase(new ArgStatic888());
131 |
132 | arguments.addCase(new ArgStatic4444());
133 | arguments.addCase(new ArgStatic4448());
134 | arguments.addCase(new ArgStatic4484());
135 | arguments.addCase(new ArgStatic4844());
136 | arguments.addCase(new ArgStatic8444());
137 |
138 | arguments.addCase(new ArgStatic8844());
139 | arguments.addCase(new ArgStatic8484());
140 | arguments.addCase(new ArgStatic8448());
141 | arguments.addCase(new ArgStatic8884());
142 | arguments.addCase(new ArgStatic8848());
143 | arguments.addCase(new ArgStatic8488());
144 | arguments.addCase(new ArgStatic4888());
145 | arguments.addCase(new ArgStatic8888());
146 |
147 | addSuite(arguments);
148 |
149 |
150 | TestSuite invokeType = new TestSuite("调用类型");
151 | invokeType.addCase(new InvokeConstructor());
152 |
153 | addSuite(invokeType);
154 |
155 | TestSuite custom = new TestSuite("自定义");
156 | CaseManager.getInstance().getCase(Case1.class);
157 | CaseManager.getInstance().getCase(Case2.class);
158 | CaseManager.getInstance().getCase(Case3.class);
159 | CaseManager.getInstance().getCase(Case4.class);
160 | CaseManager.getInstance().getCase(Case4.class);
161 | CaseManager.getInstance().getCase(Case5.class);
162 | CaseManager.getInstance().getCase(Case6.class);
163 | CaseManager.getInstance().getCase(Case7.class);
164 | CaseManager.getInstance().getCase(Case8_Activity_onCreate.class);
165 | CaseManager.getInstance().getCase(Case9_ThreadMonitor.class);
166 | CaseManager.getInstance().getCase(Case10_Default_Constructor.class);
167 | CaseManager.getInstance().getCase(Case12_MultiCallback.class);
168 |
169 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
170 | CaseManager.getInstance().getCase(Case11_SuspendAll.class);
171 | CaseManager.getInstance().getCase(Case14_GC.class);
172 | // CaseManager.getInstance().getCase(Case15_StopJit.class);
173 | }
174 |
175 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
176 | CaseManager.getInstance().getCase(Case13_FastNative.class);
177 | }
178 |
179 | // CaseManager.getInstance().getCase(Case16_SameEntry.class);
180 | CaseManager.getInstance().getCase(Case17_SameMethod.class);
181 | CaseManager.getInstance().getCase(Case18_returnConst.class);
182 |
183 | final Set> cases = CaseManager.getInstance().getCases();
184 | for (final Class> aCase : cases) {
185 | custom.addCase(new TestCase(aCase.getSimpleName()) {
186 | @Override
187 | public void test() {
188 | CaseManager.getInstance().getCase(aCase).hook();
189 | }
190 |
191 | @Override
192 | public boolean predicate() {
193 | return CaseManager.getInstance().getCase(aCase).validate();
194 | }
195 | });
196 | }
197 | addSuite(custom);
198 | }
199 |
200 | }
201 |
--------------------------------------------------------------------------------
/library/src/main/java/me/weishu/epic/art/Epic.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017, weishu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package me.weishu.epic.art;
18 |
19 | import android.os.Build;
20 |
21 | import com.taobao.android.dexposed.utility.Debug;
22 | import com.taobao.android.dexposed.utility.Logger;
23 | import com.taobao.android.dexposed.utility.Runtime;
24 |
25 | import java.lang.reflect.Constructor;
26 | import java.lang.reflect.Method;
27 | import java.lang.reflect.Modifier;
28 | import java.nio.ByteBuffer;
29 | import java.nio.ByteOrder;
30 | import java.util.HashMap;
31 | import java.util.Map;
32 | import java.util.concurrent.ConcurrentHashMap;
33 |
34 | import me.weishu.epic.art.arch.Arm64;
35 | import me.weishu.epic.art.arch.ShellCode;
36 | import me.weishu.epic.art.arch.Thumb2;
37 | import me.weishu.epic.art.method.ArtMethod;
38 |
39 | /**
40 | * Hook Center.
41 | */
42 | public final class Epic {
43 |
44 | private static final String TAG = "Epic";
45 |
46 | private static final Map backupMethodsMapping = new ConcurrentHashMap<>();
47 |
48 | private static final Map originSigs = new ConcurrentHashMap<>();
49 |
50 | private static final Map scripts = new HashMap<>();
51 | private static ShellCode ShellCode;
52 |
53 | static {
54 | boolean isArm = true; // TODO: 17/11/21 TODO
55 | int apiLevel = Build.VERSION.SDK_INT;
56 | boolean thumb2 = true;
57 | if (isArm) {
58 | if (Runtime.is64Bit()) {
59 | ShellCode = new Arm64();
60 | } else if (Runtime.isThumb2()) {
61 | ShellCode = new Thumb2();
62 | } else {
63 | thumb2 = false;
64 | ShellCode = new Thumb2();
65 | Logger.w(TAG, "ARM32, not support now.");
66 | }
67 | }
68 | if (ShellCode == null) {
69 | throw new RuntimeException("Do not support this ARCH now!! API LEVEL:" + apiLevel + " thumb2 ? : " + thumb2);
70 | }
71 | Logger.i(TAG, "Using: " + ShellCode.getName());
72 | }
73 |
74 | public static boolean hookMethod(Constructor origin) {
75 | return hookMethod(ArtMethod.of(origin));
76 | }
77 |
78 | public static boolean hookMethod(Method origin) {
79 | ArtMethod artOrigin = ArtMethod.of(origin);
80 | return hookMethod(artOrigin);
81 | }
82 |
83 | private static boolean hookMethod(ArtMethod artOrigin) {
84 |
85 | MethodInfo methodInfo = new MethodInfo();
86 | methodInfo.isStatic = Modifier.isStatic(artOrigin.getModifiers());
87 | final Class>[] parameterTypes = artOrigin.getParameterTypes();
88 | if (parameterTypes != null) {
89 | methodInfo.paramNumber = parameterTypes.length;
90 | methodInfo.paramTypes = parameterTypes;
91 | } else {
92 | methodInfo.paramNumber = 0;
93 | methodInfo.paramTypes = new Class>[0];
94 | }
95 | methodInfo.returnType = artOrigin.getReturnType();
96 | methodInfo.method = artOrigin;
97 | originSigs.put(artOrigin.getAddress(), methodInfo);
98 |
99 | if (!artOrigin.isAccessible()) {
100 | artOrigin.setAccessible(true);
101 | }
102 |
103 | artOrigin.ensureResolved();
104 |
105 | long originEntry = artOrigin.getEntryPointFromQuickCompiledCode();
106 | if (originEntry == ArtMethod.getQuickToInterpreterBridge()) {
107 | Logger.i(TAG, "this method is not compiled, compile it now. current entry: 0x" + Long.toHexString(originEntry));
108 | boolean ret = artOrigin.compile();
109 | if (ret) {
110 | originEntry = artOrigin.getEntryPointFromQuickCompiledCode();
111 | Logger.i(TAG, "compile method success, new entry: 0x" + Long.toHexString(originEntry));
112 | } else {
113 | Logger.e(TAG, "compile method failed...");
114 | return false;
115 | // return hookInterpreterBridge(artOrigin);
116 | }
117 | }
118 |
119 | ArtMethod backupMethod = artOrigin.backup();
120 |
121 | Logger.i(TAG, "backup method address:" + Debug.addrHex(backupMethod.getAddress()));
122 | Logger.i(TAG, "backup method entry :" + Debug.addrHex(backupMethod.getEntryPointFromQuickCompiledCode()));
123 |
124 | ArtMethod backupList = getBackMethod(artOrigin);
125 | if (backupList == null) {
126 | setBackMethod(artOrigin, backupMethod);
127 | }
128 |
129 | final long key = originEntry;
130 | final EntryLock lock = EntryLock.obtain(originEntry);
131 | //noinspection SynchronizationOnLocalVariableOrMethodParameter
132 | synchronized (lock) {
133 | if (!scripts.containsKey(key)) {
134 | scripts.put(key, new Trampoline(ShellCode, originEntry));
135 | }
136 | Trampoline trampoline = scripts.get(key);
137 | boolean ret = trampoline.install(artOrigin);
138 | // Logger.d(TAG, "hook Method result:" + ret);
139 | return ret;
140 | }
141 | }
142 |
143 | /*
144 | private static boolean hookInterpreterBridge(ArtMethod artOrigin) {
145 |
146 | String identifier = artOrigin.getIdentifier();
147 | ArtMethod backupMethod = artOrigin.backup();
148 |
149 | Logger.d(TAG, "backup method address:" + Debug.addrHex(backupMethod.getAddress()));
150 | Logger.d(TAG, "backup method entry :" + Debug.addrHex(backupMethod.getEntryPointFromQuickCompiledCode()));
151 |
152 | List backupList = backupMethodsMapping.get(identifier);
153 | if (backupList == null) {
154 | backupList = new LinkedList();
155 | backupMethodsMapping.put(identifier, backupList);
156 | }
157 | backupList.add(backupMethod);
158 |
159 | long originalEntryPoint = ShellCode.toMem(artOrigin.getEntryPointFromQuickCompiledCode());
160 | Logger.d(TAG, "originEntry Point(bridge):" + Debug.addrHex(originalEntryPoint));
161 |
162 | originalEntryPoint += 16;
163 | Logger.d(TAG, "originEntry Point(offset8):" + Debug.addrHex(originalEntryPoint));
164 |
165 | if (!scripts.containsKey(originalEntryPoint)) {
166 | scripts.put(originalEntryPoint, new Trampoline(ShellCode, artOrigin));
167 | }
168 | Trampoline trampoline = scripts.get(originalEntryPoint);
169 |
170 | boolean ret = trampoline.install();
171 | Logger.i(TAG, "hook Method result:" + ret);
172 | return ret;
173 |
174 | }*/
175 |
176 | public synchronized static ArtMethod getBackMethod(ArtMethod origin) {
177 | String identifier = origin.getIdentifier();
178 | return backupMethodsMapping.get(identifier);
179 | }
180 |
181 | public static synchronized void setBackMethod(ArtMethod origin, ArtMethod backup) {
182 | String identifier = origin.getIdentifier();
183 | backupMethodsMapping.put(identifier, backup);
184 | }
185 |
186 | public static MethodInfo getMethodInfo(long address) {
187 | return originSigs.get(address);
188 | }
189 |
190 | public static int getQuickCompiledCodeSize(ArtMethod method) {
191 |
192 | long entryPoint = ShellCode.toMem(method.getEntryPointFromQuickCompiledCode());
193 | long sizeInfo1 = entryPoint - 4;
194 | byte[] bytes = EpicNative.get(sizeInfo1, 4);
195 | int size = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getInt();
196 | Logger.d(TAG, "getQuickCompiledCodeSize: " + size);
197 | return size;
198 | }
199 |
200 |
201 | public static class MethodInfo {
202 | public boolean isStatic;
203 | public int paramNumber;
204 | public Class>[] paramTypes;
205 | public Class> returnType;
206 | public ArtMethod method;
207 |
208 | @Override
209 | public String toString() {
210 | return method.toGenericString();
211 | }
212 | }
213 |
214 | private static class EntryLock {
215 | static Map sLockPool = new HashMap<>();
216 |
217 | static synchronized EntryLock obtain(long entry) {
218 | if (sLockPool.containsKey(entry)) {
219 | return sLockPool.get(entry);
220 | } else {
221 | EntryLock entryLock = new EntryLock();
222 | sLockPool.put(entry, entryLock);
223 | return entryLock;
224 | }
225 | }
226 | }
227 | }
228 |
--------------------------------------------------------------------------------