├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── iwcode │ │ └── artandhook │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── iwcode │ │ │ ├── Logger.java │ │ │ ├── MainActivity.java │ │ │ ├── MyTest.java │ │ │ └── ReflectionUtil.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── iwcode │ └── artandhook │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradlew ├── gradlew.bat ├── jhook ├── .gitignore ├── CMakeLists.txt ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── iwcode │ │ └── jhook │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── cpp │ │ ├── art_method.h │ │ └── native-lib.cpp │ └── java │ │ └── com │ │ └── yanggs │ │ └── jhook │ │ ├── BackMethod.java │ │ ├── HookUtil.java │ │ ├── JXposed.java │ │ ├── MethodCallback.java │ │ ├── MethodHookParam.java │ │ ├── Test.java │ │ └── utils │ │ ├── ClassUtils.java │ │ ├── JXposedHelpers.java │ │ ├── MemberUtils.java │ │ └── MethodUtil.java │ └── test │ └── java │ └── com │ └── iwcode │ └── jhook │ └── ExampleUnitTest.java ├── library ├── .gitignore ├── CMakeCache.txt ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── cpp │ ├── .gitignore │ ├── CMakeLists.txt │ ├── art.cpp │ ├── art.h │ ├── build_with_cmake │ ├── epic.cpp │ ├── fake_dlfcn.cpp │ └── fake_dlfcn.h │ ├── java │ ├── com │ │ └── taobao │ │ │ └── android │ │ │ └── dexposed │ │ │ ├── ClassUtils.java │ │ │ ├── DeviceCheck.java │ │ │ ├── DexposedBridge.java │ │ │ ├── XC_MethodHook.java │ │ │ ├── XC_MethodReplacement.java │ │ │ ├── XposedHelpers.java │ │ │ ├── callbacks │ │ │ ├── IXUnhook.java │ │ │ └── XCallback.java │ │ │ └── utility │ │ │ ├── Debug.java │ │ │ ├── Logger.java │ │ │ ├── NeverCalled.java │ │ │ ├── NougatPolicy.java │ │ │ ├── Platform.java │ │ │ ├── Runtime.java │ │ │ └── Unsafe.java │ └── me │ │ └── weishu │ │ └── epic │ │ └── art │ │ ├── Epic.java │ │ ├── EpicNative.java │ │ ├── Trampoline.java │ │ ├── arch │ │ ├── Arm64.java │ │ ├── Arm64_2.java │ │ ├── ShellCode.java │ │ └── Thumb2.java │ │ ├── entry │ │ ├── Entry.java │ │ ├── Entry64.java │ │ └── Entry64_2.java │ │ └── method │ │ ├── ArtMethod.java │ │ └── Offset.java │ ├── jniLibs │ └── armeabi │ │ └── libdexposed.so │ └── res │ └── values │ └── strings.xml └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea 5 | .DS_Store 6 | /build 7 | /captures 8 | /gradle 9 | .externalNativeBuild 10 | *.out 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 因为工作需要Hook java中的方法,一开始用的epic,但是稳定性不太好,手动JIT编译有时会失败,导致闪退,并且9.0上hook貌似不管用。 3 | 4 | 于是就基于一些已有的native hook方案,做了一些改进和完善,并适配了9.0。 5 | 6 | 该方案只能hook java函数,如果hook c函数,请看[gotHook](https://github.com/shineygs/GotHook) 7 | 8 | 不过正如epic的作者说的那样,这种方案不支持Hook系统的一些函数。因为这种方案的有一个前提就是方法调用必须是先拿到ArtMethod,再去取entrypoint然后跳转实现调用。但是很多情况下,系统知道你要调用的这个方法的entrypoint是什么,直接写死在汇编代码里,这样方法调用的时候就不会有取ArtMethod这个动作,从而不会去拿被替换的entrypoint,导致Hook失效。不过对我来说已经够用了。 9 | 10 | 11 | ``` 12 | dependencies { 13 | implementation 'com.yanggs:arthook:0.2.7' 14 | } 15 | ``` 16 | 17 | 18 | ``` 19 | 20 | JXposed.findAndHookMethod(MainActivity.class, "testFun", String.class,int.class,new MethodCallback() { 21 | @Override 22 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable { 23 | super.beforeHookedMethod(param); 24 | Logger.i("beforeHookedMethod:"); 25 | } 26 | 27 | @Override 28 | protected void afterHookedMethod(MethodHookParam param) throws Throwable { 29 | super.afterHookedMethod(param); 30 | Logger.i("afterHookedMethod:"); 31 | 32 | } 33 | }); 34 | 35 | ``` 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 28 5 | defaultConfig { 6 | applicationId "com.iwcode" 7 | minSdkVersion 22 8 | targetSdkVersion 28 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | 13 | externalNativeBuild { 14 | cmake { 15 | cppFlags "-std=c++11" 16 | } 17 | ndk{ 18 | // abiFilters "armeabi","armeabi-v7a","x86" 19 | abiFilters "armeabi-v7a" 20 | } 21 | } 22 | } 23 | buildTypes { 24 | release { 25 | minifyEnabled false 26 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 27 | } 28 | } 29 | compileOptions { 30 | sourceCompatibility = '1.8' 31 | targetCompatibility = '1.8' 32 | } 33 | 34 | sourceSets{ 35 | main{ 36 | jniLibs.srcDirs = ['libs'] 37 | } 38 | } 39 | } 40 | 41 | repositories { 42 | maven{url "https://dl.bintray.com/shineygs/maven"} 43 | } 44 | 45 | dependencies { 46 | implementation fileTree(include: ['*.jar'], dir: 'libs') 47 | implementation 'com.android.support:appcompat-v7:28.0.0' 48 | implementation 'com.android.support.constraint:constraint-layout:1.1.3' 49 | testImplementation 'junit:junit:4.12' 50 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 51 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 52 | 53 | // implementation 'com.yanggs:arthook:0.2.7' 54 | implementation project(":jhook") 55 | // implementation project(":library") 56 | } 57 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/iwcode/artandhook/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.iwcode.artandhook; 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 org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.iwcode.artandhook", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/java/com/iwcode/Logger.java: -------------------------------------------------------------------------------- 1 | package com.iwcode; 2 | 3 | 4 | import android.util.Log; 5 | 6 | import java.io.File; 7 | import java.io.FileWriter; 8 | import java.io.IOException; 9 | import java.text.SimpleDateFormat; 10 | import java.util.Date; 11 | 12 | public class Logger { 13 | 14 | public static final String TAG = "JXposed"; 15 | 16 | public static final String logPath = "mnt/sdcard/Android"; 17 | 18 | public static void i(final Object o) { 19 | if (o != null){ 20 | Log.i(TAG,o.toString()); 21 | // write(TAG+" : "+o.toString()); 22 | }else{ 23 | Log.i(TAG,"null"); 24 | // write(TAG+" : null"); 25 | } 26 | } 27 | 28 | public static void i(String tag,final Object o) { 29 | if (o != null){ 30 | Log.i(tag,o.toString()); 31 | // write(tag+" : "+o.toString()); 32 | }else{ 33 | Log.i(tag,"null"); 34 | // write(tag+" : null"); 35 | } 36 | } 37 | 38 | private static void write(String log){ 39 | File configDir = new File(logPath, "vkwechat-"+getDay()+".log"); 40 | try { 41 | FileWriter writer = new FileWriter(configDir, true); 42 | writer.append("["+getTime()+"] "+log); 43 | writer.flush(); 44 | writer.append("\r\n"); 45 | writer.flush(); 46 | writer.close(); 47 | } catch (IOException e) { 48 | e.printStackTrace(); 49 | } 50 | } 51 | 52 | private static String getTime(){ 53 | Date date = new Date(); 54 | SimpleDateFormat format = new SimpleDateFormat("MM-dd HH:mm:ss"); 55 | return format.format(date); 56 | } 57 | 58 | private static String getDay(){ 59 | Date date = new Date(); 60 | SimpleDateFormat format = new SimpleDateFormat("MM-dd"); 61 | return format.format(date); 62 | } 63 | 64 | public static void clearCacheLog(){ 65 | File[] files = new File(logPath).listFiles((dir, name) -> name.endsWith(".log")); 66 | 67 | for (int i = 0; i < files.length; i++){ 68 | long lastModified = files[i].lastModified(); 69 | if (lastModified < getWeekAgo()){ 70 | files[i].delete(); 71 | Logger.i("delete log file = "+files[i].getName()); 72 | } 73 | } 74 | } 75 | 76 | public static long getWeekAgo(){ 77 | long timeMillis = System.currentTimeMillis(); 78 | timeMillis = timeMillis - 1000*60*60*24*7; 79 | return timeMillis; 80 | } 81 | 82 | public static long getMonthAgo(){ 83 | long timeMillis = System.currentTimeMillis(); 84 | timeMillis = timeMillis - 1000*60*60*24*30; 85 | return timeMillis; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /app/src/main/java/com/iwcode/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.iwcode; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | import android.util.Log; 6 | import android.widget.TextView; 7 | 8 | //import com.taobao.android.dexposed.DexposedBridge; 9 | //import com.taobao.android.dexposed.XC_MethodHook; 10 | import com.yanggs.jhook.JXposed; 11 | import com.yanggs.jhook.MethodCallback; 12 | import com.yanggs.jhook.MethodHookParam; 13 | import com.yanggs.jhook.utils.JXposedHelpers; 14 | 15 | import java.lang.reflect.Constructor; 16 | import java.lang.reflect.Method; 17 | 18 | 19 | 20 | public class MainActivity extends AppCompatActivity { 21 | 22 | // static { 23 | // System.loadLibrary("native-lib"); 24 | // } 25 | 26 | @Override 27 | protected void onCreate(Bundle savedInstanceState) { 28 | super.onCreate(savedInstanceState); 29 | setContentView(R.layout.activity_main); 30 | // 调用隐藏api 31 | // boolean b = ReflectionUtil.exemptAll(); 32 | 33 | // JXposed.findAndHookConstructor(MyTest.class, new MethodCallback() { 34 | // @Override 35 | // protected void beforeHookedMethod(MethodHookParam param) throws Throwable { 36 | // super.beforeHookedMethod(param); 37 | // Logger.i("beforeHookedMethod: MyTest Constructor"); 38 | // } 39 | // 40 | // @Override 41 | // protected void afterHookedMethod(MethodHookParam param) throws Throwable { 42 | // super.afterHookedMethod(param); 43 | // Logger.i("afterHookedMethod: MyTest Constructor"); 44 | // } 45 | // }); 46 | // 47 | // JXposed.findAndHookConstructor(MyTest.class, String.class,int.class,new MethodCallback() { 48 | // @Override 49 | // protected void beforeHookedMethod(MethodHookParam param) throws Throwable { 50 | // super.beforeHookedMethod(param); 51 | // Logger.i("beforeHookedMethod: MyTest 有参数 Constructor"); 52 | // } 53 | // 54 | // @Override 55 | // protected void afterHookedMethod(MethodHookParam param) throws Throwable { 56 | // super.afterHookedMethod(param); 57 | // Logger.i("afterHookedMethod: MyTest 有参数 Constructor"); 58 | // } 59 | // }); 60 | // 61 | // JXposed.findAndHookMethod(MyTest.class, "testFun", String.class,int.class,new MethodCallback() { 62 | // @Override 63 | // protected void beforeHookedMethod(MethodHookParam param) throws Throwable { 64 | // super.beforeHookedMethod(param); 65 | // Logger.i("beforeHookedMethod: testFun"); 66 | // } 67 | // 68 | // @Override 69 | // protected void afterHookedMethod(MethodHookParam param) throws Throwable { 70 | // super.afterHookedMethod(param); 71 | // Logger.i("afterHookedMethod: testFun"); 72 | // 73 | // } 74 | // }); 75 | // 76 | // JXposed.findAndHookMethod(MyTest.class, "testFunS", String.class,int.class,new MethodCallback() { 77 | // @Override 78 | // protected void beforeHookedMethod(MethodHookParam param) throws Throwable { 79 | // super.beforeHookedMethod(param); 80 | // Logger.i("beforeHookedMethod: testFunFinal"); 81 | // } 82 | // 83 | // @Override 84 | // protected void afterHookedMethod(MethodHookParam param) throws Throwable { 85 | // super.afterHookedMethod(param); 86 | // Logger.i("afterHookedMethod: testFunFinal"); 87 | // 88 | // } 89 | // }); 90 | 91 | // JXposed.findAndHookMethod(MainActivity.class, "abc",new MethodCallback() { 92 | // @Override 93 | // protected void beforeHookedMethod(MethodHookParam param) throws Throwable { 94 | // super.beforeHookedMethod(param); 95 | // Logger.i("beforeHookedMethod: abc"); 96 | // } 97 | // 98 | // @Override 99 | // protected void afterHookedMethod(MethodHookParam param) throws Throwable { 100 | // super.afterHookedMethod(param); 101 | // Logger.i("afterHookedMethod: abc"); 102 | // param.setResult("123"); 103 | // 104 | // } 105 | // }); 106 | 107 | JXposed.findAndHookMethod(TextView.class, "setText",CharSequence.class,new MethodCallback() { 108 | @Override 109 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable { 110 | super.beforeHookedMethod(param); 111 | Logger.i("beforeHookedMethod: setText" + param.args[0]); 112 | } 113 | 114 | @Override 115 | protected void afterHookedMethod(MethodHookParam param) throws Throwable { 116 | super.afterHookedMethod(param); 117 | Logger.i("afterHookedMethod: setText"); 118 | } 119 | }); 120 | 121 | ((TextView)findViewById(R.id.sample_text)).setText("hello holle"); 122 | 123 | // String s = abc(); 124 | // Logger.i("s = "+s); 125 | 126 | // MyTest myTest = new MyTest("ygs1",1); 127 | // myTest.testFun("ygs2",2); 128 | // myTest.testFunFinal("ygs3",3); 129 | // try{ 130 | // JXposedHelpers.callMethod(new MyTest(),"testFunS","ygs4", 4); 131 | // }catch (Exception e){ 132 | // Logger.i(e.getMessage()); 133 | // e.printStackTrace(); 134 | // } 135 | 136 | // 137 | // Logger.i("b = "+b); 138 | // 139 | // DexposedBridge.findAndHookMethod(MyTest.class, "testFunS", String.class, int.class, new XC_MethodHook() { 140 | // @Override 141 | // protected void beforeHookedMethod(MethodHookParam param) throws Throwable { 142 | // super.beforeHookedMethod(param); 143 | // Logger.i("testFunS beforeHookedMethod"); 144 | // } 145 | // 146 | // @Override 147 | // protected void afterHookedMethod(MethodHookParam param) throws Throwable { 148 | // super.afterHookedMethod(param); 149 | // Logger.i("testFunS afterHookedMethod"); 150 | // } 151 | // }); 152 | // 153 | // MyTest.testFunS("ygs",10); 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /app/src/main/java/com/iwcode/MyTest.java: -------------------------------------------------------------------------------- 1 | package com.iwcode; 2 | 3 | /** 4 | * Created by YangGuoShan on 2018/5/28. 5 | */ 6 | public class MyTest { 7 | 8 | private MyTest(int i) { 9 | Logger.i("MyTest: i = "+i); 10 | } 11 | 12 | public MyTest() { 13 | Logger.i("MyTest:"); 14 | } 15 | 16 | public MyTest(String s,int i) { 17 | Logger.i("MyTest:"+s+","+i); 18 | } 19 | 20 | public void testFun(String s,int i){ 21 | Logger.i("testFun:"+s+","+i); 22 | } 23 | 24 | public final void testFunFinal(String s,int i){ 25 | Logger.i("testFunFinal:"+s+","+i); 26 | } 27 | 28 | public void testFun2(String s,int i){ 29 | Logger.i("testFun2:"+s+","+i); 30 | } 31 | 32 | public static void testFunS(String s,int i){ 33 | Logger.i("testFunS:"+s+","+i); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/iwcode/ReflectionUtil.java: -------------------------------------------------------------------------------- 1 | package com.iwcode; 2 | 3 | import java.lang.reflect.Method; 4 | 5 | /** 6 | * Created by YangGuoShan on 2018/6/4. 7 | */ 8 | public class ReflectionUtil { 9 | 10 | private static Object sVmRuntime; 11 | private static Method setHiddenApiExemptions; 12 | 13 | static { 14 | try { 15 | Method forName = Class.class.getDeclaredMethod("forName", String.class); 16 | Method getDeclaredMethod = Class.class.getDeclaredMethod("getDeclaredMethod", String.class, Class[].class); 17 | 18 | Class vmRuntimeClass = (Class) forName.invoke(null, "dalvik.system.VMRuntime"); 19 | Method getRuntime = (Method) getDeclaredMethod.invoke(vmRuntimeClass, "getRuntime", null); 20 | setHiddenApiExemptions = (Method) getDeclaredMethod.invoke(vmRuntimeClass, "setHiddenApiExemptions", new Class[]{String[].class}); 21 | sVmRuntime = getRuntime.invoke(null); 22 | } catch (Throwable e) { 23 | Logger.i("reflect bootstrap failed: \n"+e.getMessage()); 24 | e.printStackTrace(); 25 | } 26 | } 27 | 28 | public static boolean exemptAll() { 29 | return exempt(new String[]{"L"}); 30 | } 31 | 32 | private static boolean exempt(String... methods) { 33 | if (sVmRuntime == null || setHiddenApiExemptions == null) { 34 | return false; 35 | } 36 | 37 | try { 38 | setHiddenApiExemptions.invoke(sVmRuntime, new Object[]{methods}); 39 | return true; 40 | } catch (Throwable e) { 41 | return false; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guoshan-yang/ArtAndHook/463ef871eb4d5a0ccaa5670c3f825abcd9230cac/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guoshan-yang/ArtAndHook/463ef871eb4d5a0ccaa5670c3f825abcd9230cac/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guoshan-yang/ArtAndHook/463ef871eb4d5a0ccaa5670c3f825abcd9230cac/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guoshan-yang/ArtAndHook/463ef871eb4d5a0ccaa5670c3f825abcd9230cac/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guoshan-yang/ArtAndHook/463ef871eb4d5a0ccaa5670c3f825abcd9230cac/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guoshan-yang/ArtAndHook/463ef871eb4d5a0ccaa5670c3f825abcd9230cac/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guoshan-yang/ArtAndHook/463ef871eb4d5a0ccaa5670c3f825abcd9230cac/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guoshan-yang/ArtAndHook/463ef871eb4d5a0ccaa5670c3f825abcd9230cac/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guoshan-yang/ArtAndHook/463ef871eb4d5a0ccaa5670c3f825abcd9230cac/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guoshan-yang/ArtAndHook/463ef871eb4d5a0ccaa5670c3f825abcd9230cac/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #008577 4 | #00574B 5 | #D81B60 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | ArtAndHook 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/test/java/com/iwcode/artandhook/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.iwcode.artandhook; 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() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | 5 | repositories { 6 | google() 7 | jcenter() 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:4.2.2' 11 | 12 | classpath 'com.novoda:bintray-release:0.9' 13 | 14 | // NOTE: Do not place your application dependencies here; they belong 15 | // in the individual module build.gradle files 16 | } 17 | } 18 | 19 | allprojects { 20 | repositories { 21 | google() 22 | jcenter() 23 | } 24 | 25 | //加上这些 26 | tasks.withType(Javadoc) { 27 | options{ encoding "UTF-8" 28 | charSet 'UTF-8' 29 | links "http://docs.oracle.com/javase/7/docs/api" 30 | } 31 | } 32 | } 33 | 34 | task clean(type: Delete) { 35 | delete rootProject.buildDir 36 | } 37 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | 15 | 16 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /jhook/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /.cxx 3 | -------------------------------------------------------------------------------- /jhook/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 | add_library( # Sets the name of the library. 14 | native-lib 15 | 16 | # Sets the library as a shared library. 17 | SHARED 18 | 19 | # Provides a relative path to your source file(s). 20 | src/main/cpp/native-lib.cpp) 21 | 22 | # Searches for a specified prebuilt library and stores the path as a 23 | # variable. Because CMake includes system libraries in the search path by 24 | # default, you only need to specify the name of the public NDK library 25 | # you want to add. CMake verifies that the library exists before 26 | # completing its build. 27 | 28 | find_library( # Sets the name of the path variable. 29 | log-lib 30 | 31 | # Specifies the name of the NDK library that 32 | # you want CMake to locate. 33 | log) 34 | 35 | # Specifies libraries CMake should link to your target library. You 36 | # can link multiple libraries, such as libraries you define in this 37 | # build script, prebuilt third-party libraries, or system libraries. 38 | 39 | target_link_libraries( # Specifies the target library. 40 | native-lib 41 | 42 | # Links the target library to the log library 43 | # included in the NDK. 44 | ${log-lib}) -------------------------------------------------------------------------------- /jhook/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'com.novoda.bintray-release' 3 | 4 | android { 5 | compileSdkVersion 28 6 | 7 | defaultConfig { 8 | minSdkVersion 21 9 | targetSdkVersion 28 10 | versionCode 1 11 | versionName "1.0" 12 | 13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 14 | 15 | externalNativeBuild { 16 | cmake { 17 | cppFlags "-std=c++11" 18 | } 19 | ndk{ 20 | // abiFilters "armeabi","armeabi-v7a","x86" 21 | abiFilters "armeabi-v7a" 22 | } 23 | } 24 | } 25 | 26 | buildTypes { 27 | release { 28 | minifyEnabled false 29 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 30 | } 31 | } 32 | externalNativeBuild { 33 | cmake { 34 | path "CMakeLists.txt" 35 | } 36 | } 37 | 38 | compileOptions { 39 | sourceCompatibility = '1.8' 40 | targetCompatibility = '1.8' 41 | } 42 | 43 | lintOptions { 44 | abortOnError false 45 | } 46 | } 47 | 48 | publish { 49 | repoName="maven"//你的仓库名称,没有填写默认仓库是maven//这也是很多人上传仓库不对名问题最多情况, 50 | userOrg = 'shineygs' //bintray注册的用户名 51 | groupId = 'com.yanggs' //compile引用时的第1部分groupId 52 | artifactId = 'arthook' //compile引用时的第2部分项目名 53 | publishVersion = '0.2.7' //compile引用时的第3部分版本号 54 | desc = 'art java hook method'//d项目描述 55 | website = 'https://github.com/shineygs/ArtAndHook' //github 托管地址 56 | } 57 | 58 | dependencies { 59 | implementation fileTree(dir: 'libs', include: ['*.jar']) 60 | 61 | implementation 'com.android.support:appcompat-v7:28.0.0' 62 | testImplementation 'junit:junit:4.12' 63 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 64 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 65 | implementation 'com.linkedin.dexmaker:dexmaker:2.25.0' 66 | 67 | } 68 | -------------------------------------------------------------------------------- /jhook/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /jhook/src/androidTest/java/com/iwcode/jhook/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.yanggs.jhook; 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 org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.yanggs.jhook.test", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /jhook/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /jhook/src/main/cpp/art_method.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (c) 2011 The Android Open Source Project 4 | * Copyright (c) 2016, alipay.com 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 | /** 20 | * art_7_0.h 21 | * 22 | * @author : sanping.li@alipay.com 23 | * 24 | */ 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | #include 34 | 35 | #include /* C99 */ 36 | 37 | namespace art { 38 | namespace mirror { 39 | class Object { 40 | public: 41 | // The number of vtable entries in java.lang.Object. 42 | static constexpr size_t kVTableLength = 11; 43 | static uint32_t hash_code_seed; 44 | uint32_t klass_; 45 | 46 | uint32_t monitor_; 47 | }; 48 | 49 | class Class: public Object { 50 | public: 51 | enum Status { 52 | kStatusRetired = -2, // Retired, should not be used. Use the newly cloned one instead. 53 | kStatusError = -1, 54 | kStatusNotReady = 0, 55 | kStatusIdx = 1, // Loaded, DEX idx in super_class_type_idx_ and interfaces_type_idx_. 56 | kStatusLoaded = 2, // DEX idx values resolved. 57 | kStatusResolving = 3, // Just cloned from temporary class object. 58 | kStatusResolved = 4, // Part of linking. 59 | kStatusVerifying = 5, // In the process of being verified. 60 | kStatusRetryVerificationAtRuntime = 6, // Compile time verification failed, retry at runtime. 61 | kStatusVerifyingAtRuntime = 7, // Retrying verification at runtime. 62 | kStatusVerified = 8, // Logically part of linking; done pre-init. 63 | kStatusInitializing = 9, // Class init in progress. 64 | kStatusInitialized = 10, // Ready to go. 65 | kStatusMax = 11, 66 | }; 67 | 68 | // A magic value for reference_instance_offsets_. Ignore the bits and walk the super chain when 69 | // this is the value. 70 | // [This is an unlikely "natural" value, since it would be 30 non-ref instance fields followed by 71 | // 2 ref instance fields.] 72 | static constexpr uint32_t kClassWalkSuper = 0xC0000000; 73 | // Interface method table size. Increasing this value reduces the chance of two interface methods 74 | // colliding in the interface method table but increases the size of classes that implement 75 | // (non-marker) interfaces. 76 | static constexpr size_t kImtSize = 0; //IMT_SIZE; 77 | // 'Class' Object Fields 78 | // Order governed by java field ordering. See art::ClassLinker::LinkFields. 79 | uint32_t annotation_type_; 80 | 81 | // Defining class loader, or null for the "bootstrap" system loader. 82 | uint32_t class_loader_; 83 | // For array classes, the component class object for instanceof/checkcast 84 | // (for String[][][], this will be String[][]). null for non-array classes. 85 | uint32_t component_type_; 86 | // DexCache of resolved constant pool entries (will be null for classes generated by the 87 | // runtime such as arrays and primitive classes). 88 | uint32_t dex_cache_; 89 | // The interface table (iftable_) contains pairs of a interface class and an array of the 90 | // interface methods. There is one pair per interface supported by this class. That means one 91 | // pair for each interface we support directly, indirectly via superclass, or indirectly via a 92 | // superinterface. This will be null if neither we nor our superclass implement any interfaces. 93 | // 94 | // Why we need this: given "class Foo implements Face", declare "Face faceObj = new Foo()". 95 | // Invoke faceObj.blah(), where "blah" is part of the Face interface. We can't easily use a 96 | // single vtable. 97 | // 98 | // For every interface a concrete class implements, we create an array of the concrete vtable_ 99 | // methods for the methods in the interface. 100 | uint32_t iftable_; 101 | // Descriptor for the class such as "java.lang.Class" or "[C". Lazily initialized by ComputeName 102 | uint32_t name_; 103 | // The superclass, or null if this is java.lang.Object or a primitive type. 104 | // 105 | // Note that interfaces have java.lang.Object as their 106 | // superclass. This doesn't match the expectations in JNI 107 | // GetSuperClass or java.lang.Class.getSuperClass() which need to 108 | // check for interfaces and return null. 109 | uint32_t super_class_; 110 | 111 | // If class verify fails, we must return same error on subsequent tries. We may store either 112 | // the class of the error, or an actual instance of Throwable here. 113 | uint32_t verify_error_; 114 | // Virtual method table (vtable), for use by "invoke-virtual". The vtable from the superclass is 115 | // copied in, and virtual methods from our class either replace those from the super or are 116 | // appended. For abstract classes, methods may be created in the vtable that aren't in 117 | // virtual_ methods_ for miranda methods. 118 | uint32_t vtable_; 119 | // Access flags; low 16 bits are defined by VM spec. 120 | // Note: Shuffled back. 121 | uint32_t access_flags_; 122 | // Short cuts to dex_cache_ member for fast compiled code access. 123 | uint64_t dex_cache_strings_; 124 | 125 | // instance fields 126 | // 127 | // These describe the layout of the contents of an Object. 128 | // Note that only the fields directly declared by this class are 129 | // listed in ifields; fields declared by a superclass are listed in 130 | // the superclass's Class.ifields. 131 | // 132 | // ArtFields are allocated as a length prefixed ArtField array, and not an array of pointers to 133 | // ArtFields. 134 | uint64_t ifields_; 135 | // Pointer to an ArtMethod length-prefixed array. All the methods where this class is the place 136 | // where they are logically defined. This includes all private, static, final and virtual methods 137 | // as well as inherited default methods and miranda methods. 138 | // 139 | // The slice methods_ [0, virtual_methods_offset_) are the direct (static, private, init) methods 140 | // declared by this class. 141 | // 142 | // The slice methods_ [virtual_methods_offset_, copied_methods_offset_) are the virtual methods 143 | // declared by this class. 144 | // 145 | // The slice methods_ [copied_methods_offset_, |methods_|) are the methods that are copied from 146 | // interfaces such as miranda or default methods. These are copied for resolution purposes as this 147 | // class is where they are (logically) declared as far as the virtual dispatch is concerned. 148 | // 149 | // Note that this field is used by the native debugger as the unique identifier for the type. 150 | uint64_t methods_; 151 | 152 | // Static fields length-prefixed array. 153 | uint64_t sfields_; 154 | 155 | // Class flags to help speed up visiting object references. 156 | uint32_t class_flags_; 157 | 158 | // Total size of the Class instance; used when allocating storage on gc heap. 159 | // See also object_size_. 160 | uint32_t class_size_; 161 | // Tid used to check for recursive invocation. 162 | pid_t clinit_thread_id_; 163 | // ClassDef index in dex file, -1 if no class definition such as an array. 164 | // TODO: really 16bits 165 | int32_t dex_class_def_idx_; 166 | // Type index in dex file. 167 | // TODO: really 16bits 168 | int32_t dex_type_idx_; 169 | // Number of instance fields that are object refs. 170 | uint32_t num_reference_instance_fields_; 171 | // Number of static fields that are object refs, 172 | uint32_t num_reference_static_fields_; 173 | // Total object size; used when allocating storage on gc heap. 174 | // (For interfaces and abstract classes this will be zero.) 175 | // See also class_size_. 176 | uint32_t object_size_; 177 | // The lower 16 bits contains a Primitive::Type value. The upper 16 178 | // bits contains the size shift of the primitive type. 179 | uint32_t primitive_type_; 180 | // Bitmap of offsets of ifields. 181 | uint32_t reference_instance_offsets_; 182 | // State of class initialization. 183 | Status status_; 184 | // The offset of the first virtual method that is copied from an interface. This includes miranda, 185 | // default, and default-conflict methods. Having a hard limit of ((2 << 16) - 1) for methods 186 | // defined on a single class is well established in Java so we will use only uint16_t's here. 187 | uint16_t copied_methods_offset_; 188 | 189 | // The offset of the first declared virtual methods in the methods_ array. 190 | uint16_t virtual_methods_offset_; 191 | // TODO: ? 192 | // initiating class loader list 193 | // NOTE: for classes with low serialNumber, these are unused, and the 194 | // values are kept in a table in gDvm. 195 | // InitiatingLoaderList initiating_loader_list_; 196 | // The following data exist in real class objects. 197 | // Embedded Imtable, for class object that's not an interface, fixed size. 198 | // ImTableEntry embedded_imtable_[0]; 199 | // Embedded Vtable, for class object that's not an interface, variable size. 200 | // VTableEntry embedded_vtable_[0]; 201 | // Static fields, variable size. 202 | // uint32_t fields_[0]; 203 | // java.lang.Class 204 | static uint32_t java_lang_Class_; 205 | }; 206 | 207 | class ArtField { 208 | public: 209 | uint32_t declaring_class_; 210 | uint32_t access_flags_; 211 | uint32_t field_dex_idx_; 212 | uint32_t offset_; 213 | }; 214 | 215 | class ArtMethod { 216 | public: 217 | 218 | // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses". 219 | // The class we are a part of. 220 | uint32_t declaring_class_; 221 | // Access flags; low 16 bits are defined by spec. 222 | uint32_t access_flags_; 223 | /* Dex file fields. The defining dex file is available via declaring_class_->dex_cache_ */ 224 | // Offset to the CodeItem. 225 | uint32_t dex_code_item_offset_; 226 | // Index into method_ids of the dex file associated with this method. 227 | uint32_t dex_method_index_; 228 | /* End of dex file fields. */ 229 | // Entry within a dispatch table for this method. For static/direct methods the index is into 230 | // the declaringClass.directMethods, for virtual methods the vtable and for interface methods the 231 | // ifTable. 232 | uint16_t method_index_; 233 | 234 | // The hotness we measure for this method. Incremented by the interpreter. Not atomic, as we allow 235 | // missing increments: if the method is hot, we will see it eventually. 236 | uint16_t hotness_count_; 237 | // Fake padding field gets inserted here. 238 | // Must be the last fields in the method. 239 | // PACKED(4) is necessary for the correctness of 240 | // RoundUp(OFFSETOF_MEMBER(ArtMethod, ptr_sized_fields_), pointer_size). 241 | struct PtrSizedFields { 242 | // Short cuts to declaring_class_->dex_cache_ member for fast compiled code access. 243 | ArtMethod** dex_cache_resolved_methods_; 244 | 245 | // Short cuts to declaring_class_->dex_cache_ member for fast compiled code access. 246 | void* dex_cache_resolved_types_; 247 | 248 | // Pointer to JNI function registered to this method, or a function to resolve the JNI function, 249 | // or the profiling data for non-native methods, or an ImtConflictTable. 250 | void* entry_point_from_jni_; 251 | 252 | // Method dispatch from quick compiled code invokes this pointer which may cause bridging into 253 | // the interpreter. 254 | void* entry_point_from_quick_compiled_code_; 255 | } ptr_sized_fields_; 256 | 257 | }; 258 | 259 | } 260 | 261 | } 262 | -------------------------------------------------------------------------------- /jhook/src/main/cpp/native-lib.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "art_method.h" 10 | 11 | #define LOG_TAG "JXposed" 12 | #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__) 13 | 14 | static size_t methsize; 15 | static int supperOffset = -1; 16 | static int api_level; 17 | 18 | 19 | extern "C" JNIEXPORT void 20 | JNICALL 21 | Java_com_yanggs_jhook_HookUtil_replaceNativeArt(JNIEnv* env, jclass clazz,jobject src, jobject new_,jobject invoker) { 22 | 23 | // void* mSrc=(void*)env->FromReflectedMethod(src); 24 | // void* mNew_= env->FromReflectedMethod(new_); 25 | // void* mInvoker=env->FromReflectedMethod(invoker); 26 | art::mirror::ArtMethod* mSrc = (art::mirror::ArtMethod*)env->FromReflectedMethod(src); 27 | art::mirror::ArtMethod* mNew_ = (art::mirror::ArtMethod*)env->FromReflectedMethod(new_); 28 | art::mirror::ArtMethod* mInvoker = (art::mirror::ArtMethod*)env->FromReflectedMethod(invoker); 29 | 30 | memcpy(mInvoker, mSrc, methsize); 31 | mInvoker->access_flags_ = mInvoker->access_flags_ | 0x0002; 32 | memcpy(mSrc, mNew_, methsize); 33 | } 34 | 35 | JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) { 36 | JNIEnv* env = NULL; 37 | if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { 38 | return -1; 39 | } 40 | 41 | size_t firMid = (size_t) env->GetMethodID(env->FindClass("com/yanggs/jhook/Test"), "f1", "()V"); 42 | size_t secMid = (size_t) env->GetMethodID(env->FindClass("com/yanggs/jhook/Test"), "f2", "()V"); 43 | 44 | methsize = secMid - firMid; 45 | 46 | char api_level_str[5]; 47 | __system_property_get("ro.build.version.sdk", api_level_str); 48 | api_level = atoi(api_level_str); 49 | return JNI_VERSION_1_4; 50 | } 51 | 52 | 53 | extern "C" JNIEXPORT void 54 | JNICALL 55 | Java_com_yanggs_jhook_HookUtil_computeSupperCls(JNIEnv* env, jclass clazz,jobject fld, jobject test) { 56 | 57 | art::mirror::ArtField* field=(art::mirror::ArtField*)env->FromReflectedField(fld); 58 | art::mirror::ArtField* demo=(art::mirror::ArtField*)env->FromReflectedField(test); 59 | 60 | uint32_t *dCls=(uint32_t *)field->declaring_class_; 61 | uint32_t *hCls=(uint32_t *)demo->declaring_class_; 62 | 63 | LOGD("computeSupperCls = %p, %p",dCls,hCls); 64 | for(int i=0;i<50;++i){ 65 | if(*(dCls+i)==NULL&&*(hCls+i)==(uint32_t)dCls){ 66 | supperOffset=i; 67 | return; 68 | } 69 | } 70 | } 71 | 72 | extern "C" 73 | JNIEXPORT void JNICALL 74 | Java_com_yanggs_jhook_HookUtil_setSupperCls(JNIEnv *env, jclass type, jobject flag) { 75 | 76 | art::mirror::ArtField* field=(art::mirror::ArtField*)env->FromReflectedField(flag); 77 | size_t *dCls=(size_t *)field->declaring_class_; 78 | LOGD("supperOffset1 = %d",supperOffset); 79 | if (supperOffset == -1){ 80 | if (api_level == 28 || api_level == 27 || api_level == 26){ 81 | supperOffset = 10; 82 | }else if (api_level == 25){ 83 | supperOffset = 8; 84 | }else if (api_level == 24 || api_level == 23){ 85 | supperOffset = 9; 86 | }else if (api_level == 22 || api_level == 21){ 87 | supperOffset = 11; 88 | } 89 | } 90 | LOGD("supperOffset2 = %d",supperOffset); 91 | *(dCls + supperOffset) = NULL; 92 | } -------------------------------------------------------------------------------- /jhook/src/main/java/com/yanggs/jhook/BackMethod.java: -------------------------------------------------------------------------------- 1 | package com.yanggs.jhook; 2 | 3 | import java.lang.reflect.Member; 4 | import java.lang.reflect.Method; 5 | 6 | /** 7 | * Created by YangGuoShan on 2018/5/24. 8 | */ 9 | 10 | public class BackMethod { 11 | public Method getInvoker() { 12 | return invoker; 13 | } 14 | 15 | public void setInvoker(Method invoker) { 16 | this.invoker = invoker; 17 | } 18 | 19 | private Method invoker; 20 | 21 | private Member oldMethod; 22 | 23 | public Member getNewMethod() { 24 | return newMethod; 25 | } 26 | 27 | public void setNewMethod(Member newMethod) { 28 | this.newMethod = newMethod; 29 | } 30 | 31 | private Member newMethod; 32 | 33 | public void setBackAddr(long backAddr) { 34 | this.backAddr = backAddr; 35 | } 36 | 37 | public void setOldMethod(Member oldMethod) { 38 | this.oldMethod = oldMethod; 39 | } 40 | 41 | public long getBackAddr() { 42 | return backAddr; 43 | } 44 | 45 | public Member getOldMethod() { 46 | return oldMethod; 47 | } 48 | 49 | private long backAddr; 50 | 51 | public Object getCallback() { 52 | return callback; 53 | } 54 | 55 | public void setCallback(Object callback) { 56 | this.callback = callback; 57 | } 58 | 59 | private Object callback; 60 | 61 | 62 | } 63 | -------------------------------------------------------------------------------- /jhook/src/main/java/com/yanggs/jhook/HookUtil.java: -------------------------------------------------------------------------------- 1 | package com.yanggs.jhook; 2 | 3 | import android.util.Log; 4 | 5 | import java.lang.reflect.Field; 6 | import java.lang.reflect.Member; 7 | import java.lang.reflect.Method; 8 | 9 | /** 10 | * Created by YangGuoShan on 2018/5/27. 11 | */ 12 | public class HookUtil { 13 | 14 | int test; 15 | 16 | static { 17 | System.loadLibrary("native-lib"); 18 | try{ 19 | if (HookUtil.isArt()){ 20 | computeSupperCls(Object.class.getDeclaredFields()[0], HookUtil.class.getDeclaredField("test")); 21 | } 22 | }catch (Exception e){e.printStackTrace();} 23 | } 24 | 25 | static void replaceMethod(BackMethod old){ 26 | replaceNativeArt(old.getOldMethod(), old.getNewMethod(),old.getInvoker()); 27 | } 28 | 29 | static boolean isArt(){ 30 | final String vmVersion = System.getProperty("java.vm.version"); 31 | return vmVersion != null && vmVersion.startsWith("2"); 32 | } 33 | public static boolean setMadeClassSuper(Class cls){ 34 | try{ 35 | Field flag=cls.getField("flag"); 36 | setSupperCls(flag); 37 | return true; 38 | }catch (Exception e){ 39 | e.printStackTrace(); 40 | return false; 41 | } 42 | } 43 | private static native void replaceNativeArt(Member oldMethod, Member newMethod, Method invoker); 44 | private static native void computeSupperCls(Member fld, Member test); 45 | private static native void setSupperCls(Member flag); 46 | } 47 | -------------------------------------------------------------------------------- /jhook/src/main/java/com/yanggs/jhook/JXposed.java: -------------------------------------------------------------------------------- 1 | package com.yanggs.jhook; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | import android.util.Log; 6 | 7 | import com.android.dx.DexMaker; 8 | import com.android.dx.TypeId; 9 | import com.yanggs.jhook.utils.JXposedHelpers; 10 | import com.yanggs.jhook.utils.MethodUtil; 11 | 12 | import java.io.File; 13 | import java.io.FileNotFoundException; 14 | import java.lang.reflect.Constructor; 15 | import java.lang.reflect.Member; 16 | import java.lang.reflect.Method; 17 | import java.lang.reflect.Modifier; 18 | import java.util.HashMap; 19 | 20 | /** 21 | * Created by YangGuoShan on 2018/5/24. 22 | */ 23 | public class JXposed { 24 | 25 | private static HashMap hooked=new HashMap(); 26 | 27 | public static void findAndHookMethod(Class cla,String methodName,Object... parameterTypesAndCallback) { 28 | initContext(); 29 | if (parameterTypesAndCallback.length == 0 || !(parameterTypesAndCallback[parameterTypesAndCallback.length-1] instanceof MethodCallback)) 30 | throw new IllegalArgumentException("no callback defined"); 31 | try{ 32 | MethodCallback callback = (MethodCallback) parameterTypesAndCallback[parameterTypesAndCallback.length-1]; 33 | Method m = cla.getDeclaredMethod(methodName, JXposedHelpers.getParameterClasses(cla.getClassLoader(), parameterTypesAndCallback)); 34 | 35 | BackMethod back = new BackMethod(); 36 | back.setOldMethod(m); 37 | back.setCallback(callback); 38 | beginHook(back); 39 | }catch (Exception e){ 40 | e.printStackTrace(); 41 | Log.i("JXposed",e.getClass()+": "+e.getMessage()); 42 | Log.d("JXposed", Log.getStackTraceString(new Throwable())); 43 | throw new RuntimeException(e.getMessage()); 44 | } 45 | } 46 | 47 | public static void findAndHookConstructor(Class clazz,Object... parameterTypesAndCallback) { 48 | initContext(); 49 | if (parameterTypesAndCallback.length == 0 || !(parameterTypesAndCallback[parameterTypesAndCallback.length-1] instanceof MethodCallback)) 50 | throw new IllegalArgumentException("no callback defined"); 51 | try{ 52 | MethodCallback callback = (MethodCallback) parameterTypesAndCallback[parameterTypesAndCallback.length-1]; 53 | Constructor m = clazz.getDeclaredConstructor(JXposedHelpers.getParameterClasses(clazz.getClassLoader(), parameterTypesAndCallback)); 54 | BackMethod back=new BackMethod(); 55 | back.setOldMethod(m); 56 | back.setCallback(callback); 57 | beginHook(back); 58 | }catch (Exception e){ 59 | e.printStackTrace(); 60 | Log.i("JXposed",e.getClass()+": "+e.getMessage()); 61 | Log.d("JXposed", Log.getStackTraceString(new Throwable())); 62 | throw new RuntimeException(e.getMessage()); 63 | } 64 | } 65 | 66 | private static void beginHook(BackMethod backMethod) throws Exception { 67 | 68 | String sigName=backMethod.getOldMethod().getDeclaringClass().getSimpleName()+"_"+MethodUtil.sign(backMethod.getOldMethod()); 69 | 70 | if(hooked.get(sigName)!=null){ 71 | return; 72 | } 73 | 74 | if (HookUtil.isArt()){ 75 | DexMaker dexMaker = new DexMaker(); 76 | String className=backMethod.getOldMethod().getDeclaringClass().getName().replace(".","_"); 77 | TypeId cls = TypeId.get("L"+className+";"); 78 | Class target = backMethod.getOldMethod().getDeclaringClass(); 79 | if(Modifier.isFinal(target.getModifiers()) || Modifier.isFinal(backMethod.getOldMethod().getModifiers())) { 80 | dexMaker.declare(cls, "", Modifier.PUBLIC, TypeId.OBJECT); 81 | }else { 82 | dexMaker.declare(cls, "", Modifier.PUBLIC, TypeId.get(target)); 83 | } 84 | MethodUtil.addDefaultInstanceField(dexMaker,cls); 85 | 86 | if(backMethod.getOldMethod() instanceof Method) { 87 | MethodUtil.generateMethodFromMethod(dexMaker, cls, (Method) backMethod.getOldMethod()); 88 | MethodUtil.generateInvokerFromMethod(dexMaker, cls, (Method) backMethod.getOldMethod()); 89 | MethodUtil.addDefaultConstructor(dexMaker, cls); 90 | }else { 91 | MethodUtil.generateMethodFromConstructor(dexMaker, cls, (Constructor) backMethod.getOldMethod()); 92 | MethodUtil.generateInvokerFromConstructor(dexMaker, cls, (Constructor) backMethod.getOldMethod()); 93 | int parameterCount = ((Constructor) backMethod.getOldMethod()).getParameterTypes().length; 94 | if (parameterCount>0){ 95 | MethodUtil.addDefaultConstructor(dexMaker, cls); 96 | } 97 | } 98 | 99 | File outputDir = new File(context.getDir("path", Context.MODE_PRIVATE).getPath()); 100 | File outJarFile = new File(outputDir, (String) JXposedHelpers.callMethod(dexMaker,"generateFileName")); 101 | 102 | if (outJarFile.exists()) { 103 | outJarFile.delete(); 104 | } 105 | 106 | Log.i("JXposed","outJarFile = " + outJarFile.getAbsolutePath()); 107 | 108 | ClassLoader loader = dexMaker.generateAndLoad(context.getClassLoader(), outputDir); 109 | Class aClass = loader.loadClass(className); 110 | 111 | if (backMethod.getOldMethod() instanceof Method){ 112 | Constructor con=aClass.getDeclaredConstructor(); 113 | con.newInstance(); 114 | } 115 | 116 | Member mem=null; 117 | Method invoker=null; 118 | 119 | if (!HookUtil.setMadeClassSuper(aClass)) { 120 | throw new FileNotFoundException("found error!"); 121 | } 122 | 123 | if(backMethod.getOldMethod() instanceof Method){ 124 | mem=aClass.getDeclaredMethod(backMethod.getOldMethod().getName(),((Method) backMethod.getOldMethod()).getParameterTypes()); 125 | invoker=aClass.getDeclaredMethod(backMethod.getOldMethod().getName()+"_Invoker",((Method) backMethod.getOldMethod()).getParameterTypes()); 126 | }else{ 127 | mem=aClass.getDeclaredConstructor(((Constructor) backMethod.getOldMethod()).getParameterTypes()); 128 | invoker=aClass.getDeclaredMethod("init_Invoker",((Constructor) backMethod.getOldMethod()).getParameterTypes()); 129 | } 130 | 131 | backMethod.setInvoker(invoker); 132 | backMethod.setNewMethod(mem); 133 | HookUtil.replaceMethod(backMethod); 134 | 135 | hooked.put(mem.getDeclaringClass().getSimpleName()+"_"+MethodUtil.sign(backMethod.getOldMethod()),backMethod); 136 | }else{ 137 | throw new RuntimeException("Only support art"); 138 | } 139 | } 140 | 141 | public static Object invoke(String method,Object thiz,Object[] args)throws Throwable { 142 | Method old=null; 143 | MethodCallback callback=null; 144 | BackMethod back =(BackMethod) hooked.get(method); 145 | if(back==null){ 146 | throw new NullPointerException("find back null"); 147 | } 148 | callback=(MethodCallback) back.getCallback(); 149 | if(callback==null){ 150 | throw new NullPointerException("find old Method null"); 151 | } 152 | old= back.getInvoker(); 153 | if(old==null){ 154 | throw new NullPointerException("find old Method null"); 155 | } 156 | 157 | old.setAccessible(true); 158 | 159 | MethodHookParam param=new MethodHookParam(); 160 | param.method = old; 161 | param.thisObject = thiz; 162 | param.args = args; 163 | try { 164 | callback.beforeHookedMethod(param); 165 | } catch (Throwable t) { 166 | // reset result (ignoring what the unexpectedly exiting callback did) 167 | t.printStackTrace(); 168 | param.setResult(null); 169 | param.returnEarly = false; 170 | } 171 | if (param.getThrowable()!=null) { 172 | throw param.getThrowable(); 173 | } 174 | if (param.returnEarly) { 175 | return param.getResult(); 176 | } 177 | 178 | Object res = old.invoke(thiz, args); 179 | param.setResult(res); 180 | try { 181 | callback.afterHookedMethod(param); 182 | } catch (Throwable t) { 183 | param.setResult(null); 184 | } 185 | if (param.getThrowable()!=null) { 186 | throw param.getThrowable(); 187 | } 188 | return param.getResult(); 189 | } 190 | 191 | private static Context context=null; 192 | 193 | public static Context initContext() { 194 | if (context == null) { 195 | try { 196 | Class at = Class.forName("android.app.ActivityThread"); 197 | Application current = (Application)at.getDeclaredMethod("currentApplication").invoke(null); 198 | context = current.getBaseContext(); 199 | return context; 200 | } catch (Exception e) { 201 | e.printStackTrace(); 202 | return null; 203 | } 204 | } else { 205 | return context; 206 | } 207 | } 208 | 209 | } 210 | -------------------------------------------------------------------------------- /jhook/src/main/java/com/yanggs/jhook/MethodCallback.java: -------------------------------------------------------------------------------- 1 | package com.yanggs.jhook; 2 | 3 | /** 4 | * Created by YangGuoShan on 2018/5/24. 5 | */ 6 | public abstract class MethodCallback { 7 | public MethodCallback() { 8 | } 9 | 10 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {} 11 | protected void afterHookedMethod(MethodHookParam param) throws Throwable {} 12 | 13 | // public class MethodHookParam { 14 | // 15 | // public Member method; 16 | // 17 | // public Object thisObject; 18 | // 19 | // public Object[] args; 20 | // 21 | // private Object result = null; 22 | // private Throwable throwable = null; 23 | // boolean returnEarly = false; 24 | // 25 | // public Object getResult() { 26 | // return result; 27 | // } 28 | // 29 | // public void setResult(Object result) { 30 | // this.result = result; 31 | // this.throwable = null; 32 | // this.returnEarly = true; 33 | // } 34 | // 35 | // public Throwable getThrowable() { 36 | // return throwable; 37 | // } 38 | // 39 | // public boolean hasThrowable() { 40 | // return throwable != null; 41 | // } 42 | // 43 | // public void setThrowable(Throwable throwable) { 44 | // this.throwable = throwable; 45 | // this.result = null; 46 | // this.returnEarly = true; 47 | // } 48 | // 49 | // public Object getResultOrThrowable() throws Throwable { 50 | // if (throwable != null) 51 | // throw throwable; 52 | // return result; 53 | // } 54 | // } 55 | } 56 | -------------------------------------------------------------------------------- /jhook/src/main/java/com/yanggs/jhook/MethodHookParam.java: -------------------------------------------------------------------------------- 1 | package com.yanggs.jhook; 2 | 3 | import java.lang.reflect.Member; 4 | 5 | /** 6 | * Created by YangGuoShan on 2018/5/24. 7 | */ 8 | 9 | public class MethodHookParam { 10 | 11 | public Member method; 12 | 13 | public Object thisObject; 14 | 15 | public Object[] args; 16 | 17 | private Object result = null; 18 | private Throwable throwable = null; 19 | boolean returnEarly = false; 20 | 21 | public Object getResult() { 22 | return result; 23 | } 24 | 25 | public void setResult(Object result) { 26 | this.result = result; 27 | this.throwable = null; 28 | this.returnEarly = true; 29 | } 30 | 31 | public Throwable getThrowable() { 32 | return throwable; 33 | } 34 | 35 | public boolean hasThrowable() { 36 | return throwable != null; 37 | } 38 | 39 | public void setThrowable(Throwable throwable) { 40 | this.throwable = throwable; 41 | this.result = null; 42 | this.returnEarly = true; 43 | } 44 | 45 | public Object getResultOrThrowable() throws Throwable { 46 | if (throwable != null) 47 | throw throwable; 48 | return result; 49 | } 50 | } -------------------------------------------------------------------------------- /jhook/src/main/java/com/yanggs/jhook/Test.java: -------------------------------------------------------------------------------- 1 | package com.yanggs.jhook; 2 | 3 | public class Test { 4 | public void f1(){} 5 | public void f2(){} 6 | } 7 | -------------------------------------------------------------------------------- /jhook/src/main/java/com/yanggs/jhook/utils/ClassUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. 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 | package com.yanggs.jhook.utils; 18 | 19 | import java.util.HashMap; 20 | import java.util.Map; 21 | 22 | 23 | public class ClassUtils { 24 | 25 | public static boolean isAssignable(Class[] classArray, Class[] toClassArray, boolean autoboxing) { 26 | if (!isSameLength(classArray, toClassArray)) { 27 | return false; 28 | } 29 | if (classArray == null) { 30 | classArray = new Class[0]; 31 | } 32 | if (toClassArray == null) { 33 | toClassArray = new Class[0]; 34 | } 35 | for (int i = 0; i < classArray.length; i++) { 36 | if (!isAssignable(classArray[i], toClassArray[i], autoboxing)) { 37 | return false; 38 | } 39 | } 40 | return true; 41 | } 42 | 43 | private static boolean isSameLength(Object[] array1, Object[] array2) { 44 | if ((array1 == null && array2 != null && array2.length > 0) || 45 | (array2 == null && array1 != null && array1.length > 0) || 46 | (array1 != null && array2 != null && array1.length != array2.length)) { 47 | return false; 48 | } 49 | return true; 50 | } 51 | 52 | public static boolean isAssignable(Class cls, Class toClass, boolean autoboxing) { 53 | if (toClass == null) { 54 | return false; 55 | } 56 | // have to check for null, as isAssignableFrom doesn't 57 | if (cls == null) { 58 | return !toClass.isPrimitive(); 59 | } 60 | //autoboxing: 61 | if (autoboxing) { 62 | if (cls.isPrimitive() && !toClass.isPrimitive()) { 63 | cls = primitiveToWrapper(cls); 64 | if (cls == null) { 65 | return false; 66 | } 67 | } 68 | if (toClass.isPrimitive() && !cls.isPrimitive()) { 69 | cls = wrapperToPrimitive(cls); 70 | if (cls == null) { 71 | return false; 72 | } 73 | } 74 | } 75 | if (cls.equals(toClass)) { 76 | return true; 77 | } 78 | if (cls.isPrimitive()) { 79 | if (toClass.isPrimitive() == false) { 80 | return false; 81 | } 82 | if (Integer.TYPE.equals(cls)) { 83 | return Long.TYPE.equals(toClass) 84 | || Float.TYPE.equals(toClass) 85 | || Double.TYPE.equals(toClass); 86 | } 87 | if (Long.TYPE.equals(cls)) { 88 | return Float.TYPE.equals(toClass) 89 | || Double.TYPE.equals(toClass); 90 | } 91 | if (Boolean.TYPE.equals(cls)) { 92 | return false; 93 | } 94 | if (Double.TYPE.equals(cls)) { 95 | return false; 96 | } 97 | if (Float.TYPE.equals(cls)) { 98 | return Double.TYPE.equals(toClass); 99 | } 100 | if (Character.TYPE.equals(cls)) { 101 | return Integer.TYPE.equals(toClass) 102 | || Long.TYPE.equals(toClass) 103 | || Float.TYPE.equals(toClass) 104 | || Double.TYPE.equals(toClass); 105 | } 106 | if (Short.TYPE.equals(cls)) { 107 | return Integer.TYPE.equals(toClass) 108 | || Long.TYPE.equals(toClass) 109 | || Float.TYPE.equals(toClass) 110 | || Double.TYPE.equals(toClass); 111 | } 112 | if (Byte.TYPE.equals(cls)) { 113 | return Short.TYPE.equals(toClass) 114 | || Integer.TYPE.equals(toClass) 115 | || Long.TYPE.equals(toClass) 116 | || Float.TYPE.equals(toClass) 117 | || Double.TYPE.equals(toClass); 118 | } 119 | // should never get here 120 | return false; 121 | } 122 | return toClass.isAssignableFrom(cls); 123 | } 124 | 125 | public static Class primitiveToWrapper(Class cls) { 126 | Class convertedClass = cls; 127 | if (cls != null && cls.isPrimitive()) { 128 | convertedClass = primitiveWrapperMap.get(cls); 129 | } 130 | return convertedClass; 131 | } 132 | 133 | private static final Map, Class> primitiveWrapperMap = new HashMap, Class>(); 134 | static { 135 | primitiveWrapperMap.put(Boolean.TYPE, Boolean.class); 136 | primitiveWrapperMap.put(Byte.TYPE, Byte.class); 137 | primitiveWrapperMap.put(Character.TYPE, Character.class); 138 | primitiveWrapperMap.put(Short.TYPE, Short.class); 139 | primitiveWrapperMap.put(Integer.TYPE, Integer.class); 140 | primitiveWrapperMap.put(Long.TYPE, Long.class); 141 | primitiveWrapperMap.put(Double.TYPE, Double.class); 142 | primitiveWrapperMap.put(Float.TYPE, Float.class); 143 | primitiveWrapperMap.put(Void.TYPE, Void.TYPE); 144 | } 145 | 146 | private static final Map, Class> wrapperPrimitiveMap = new HashMap, Class>(); 147 | static { 148 | for (Class primitiveClass : primitiveWrapperMap.keySet()) { 149 | Class wrapperClass = primitiveWrapperMap.get(primitiveClass); 150 | if (!primitiveClass.equals(wrapperClass)) { 151 | wrapperPrimitiveMap.put(wrapperClass, primitiveClass); 152 | } 153 | } 154 | } 155 | public static Class wrapperToPrimitive(Class cls) { 156 | return wrapperPrimitiveMap.get(cls); 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /jhook/src/main/java/com/yanggs/jhook/utils/JXposedHelpers.java: -------------------------------------------------------------------------------- 1 | package com.yanggs.jhook.utils; 2 | 3 | import com.yanggs.jhook.MethodCallback; 4 | 5 | import java.lang.reflect.Constructor; 6 | import java.lang.reflect.Field; 7 | import java.lang.reflect.Method; 8 | import java.lang.reflect.Modifier; 9 | 10 | public class JXposedHelpers { 11 | 12 | public static Class findClassIfExists(String className, ClassLoader classLoader) { 13 | if (classLoader == null) 14 | classLoader = Thread.currentThread().getContextClassLoader(); 15 | try { 16 | return classLoader.loadClass(className); 17 | } catch (ClassNotFoundException e) { 18 | e.printStackTrace(); 19 | } 20 | return null; 21 | } 22 | 23 | public static Object newInstance(Class clazz, Object... args) { 24 | try { 25 | Constructor constructor = findConstructor(clazz, getParameterTypes(args)); 26 | return constructor.newInstance(args); 27 | } catch (Exception e) { 28 | e.printStackTrace(); 29 | } 30 | return null; 31 | } 32 | 33 | public static Object newInstance(String clazz, Object... args) { 34 | try { 35 | Constructor constructor = findConstructor(Class.forName(clazz), getParameterTypes(args)); 36 | return constructor.newInstance(args); 37 | } catch (Exception e) { 38 | e.printStackTrace(); 39 | } 40 | return null; 41 | } 42 | 43 | public static Object newInstance(String clazz, Class[] types, Object... args) throws Exception { 44 | Constructor constructor = findConstructor(Class.forName(clazz), types); 45 | return constructor.newInstance(args); 46 | } 47 | 48 | public static Class findClass(String clazz) throws ClassNotFoundException { 49 | return Class.forName(clazz); 50 | } 51 | 52 | public static Object callMethod(Object obj, String methodName, Class[] parameterTypes, Object... args) { 53 | try { 54 | Method method = findMethod(obj.getClass(), methodName, parameterTypes); 55 | return method.invoke(obj, args); 56 | } catch (Exception e) { 57 | e.printStackTrace(); 58 | return null; 59 | } 60 | } 61 | 62 | public static Object callMethod(Object obj, String methodName, Object... args) { 63 | try { 64 | Method method = findMethod(obj.getClass(), methodName, getParameterTypes(args)); 65 | return method.invoke(obj, args); 66 | } catch (Exception e) { 67 | e.printStackTrace(); 68 | return null; 69 | } 70 | } 71 | 72 | public static Object callStaticMethod(Class clazz, String methodName, Object... args) { 73 | try { 74 | Method method = findMethod(clazz, methodName, getParameterTypes(args)); 75 | return method.invoke(null, args); 76 | } catch (Exception e) { 77 | e.printStackTrace(); 78 | return null; 79 | } 80 | } 81 | 82 | public static Object callStaticMethod(Class clazz, String methodName, Class[] parameterTypes, Object... args) { 83 | try { 84 | Method method = findMethod(clazz, methodName, parameterTypes); 85 | return method.invoke(null, args); 86 | } catch (Exception e) { 87 | e.printStackTrace(); 88 | return null; 89 | } 90 | } 91 | 92 | public static Object callStaticMethod(String clazz, String methodName, Object... args) { 93 | try { 94 | Method method = findMethod(Class.forName(clazz), methodName, getParameterTypes(args)); 95 | return method.invoke(null, args); 96 | } catch (Exception e) { 97 | e.printStackTrace(); 98 | return null; 99 | } 100 | } 101 | 102 | public static void setObjectField(Object obj, String fieldName, Object value) throws Exception { 103 | Field field = findField(obj.getClass(), fieldName); 104 | field.setAccessible(true); 105 | field.set(obj, value); 106 | } 107 | 108 | public static void setIntField(Object obj, String fieldName, int value) throws Exception { 109 | Field field = findField(obj.getClass(), fieldName); 110 | field.setAccessible(true); 111 | field.setInt(obj, value); 112 | } 113 | 114 | public static void setFloatField(Object obj, String fieldName, float value) throws Exception { 115 | Field field = findField(obj.getClass(), fieldName); 116 | field.setAccessible(true); 117 | field.setFloat(obj, value); 118 | } 119 | 120 | public static void setLongField(Object obj, String fieldName, long value) throws Exception { 121 | Field field = findField(obj.getClass(), fieldName); 122 | field.setAccessible(true); 123 | field.setLong(obj, value); 124 | } 125 | 126 | public static void setBooleanField(Object obj, String fieldName, boolean value) throws Exception { 127 | Field field = findField(obj.getClass(), fieldName); 128 | field.setAccessible(true); 129 | field.setBoolean(obj, value); 130 | } 131 | 132 | public static Object getObjectField(Object obj, String fieldName) { 133 | try { 134 | Field field = findField(obj.getClass(), fieldName); 135 | field.setAccessible(true); 136 | return field.get(obj); 137 | } catch (Exception e) { 138 | e.printStackTrace(); 139 | } 140 | return null; 141 | } 142 | 143 | public static long getLongField(Object obj, String fieldName) throws Exception { 144 | Field field = findField(obj.getClass(), fieldName); 145 | field.setAccessible(true); 146 | return field.getLong(obj); 147 | } 148 | 149 | public static int getIntField(Object obj, String fieldName) { 150 | try { 151 | Field field = findField(obj.getClass(), fieldName); 152 | field.setAccessible(true); 153 | return field.getInt(obj); 154 | } catch (Exception e) { 155 | e.printStackTrace(); 156 | } 157 | return -1; 158 | } 159 | 160 | public static Object getStaticObjectField(Class clazz, String fieldName) { 161 | try { 162 | Field field = findField(clazz, fieldName); 163 | field.setAccessible(true); 164 | return field.get(null); 165 | } catch (Exception e) { 166 | e.printStackTrace(); 167 | } 168 | return null; 169 | } 170 | 171 | private static Field findField(Class clazz, String fieldName) throws NoSuchFieldException { 172 | try { 173 | return clazz.getDeclaredField(fieldName); 174 | } catch (NoSuchFieldException e) { 175 | while (true) { 176 | clazz = clazz.getSuperclass(); 177 | if (clazz == null || clazz.equals(Object.class)) 178 | break; 179 | try { 180 | return clazz.getDeclaredField(fieldName); 181 | } catch (NoSuchFieldException ignored) { 182 | } 183 | } 184 | throw e; 185 | } 186 | } 187 | 188 | private static Method findMethod(Class clazz, String methodName, Class[] parameterTypes) { 189 | try { 190 | Method method = clazz.getDeclaredMethod(methodName, parameterTypes); 191 | method.setAccessible(true); 192 | return method; 193 | } catch (NoSuchMethodException e) { 194 | } 195 | 196 | Method bestMatch = null; 197 | Class clz = clazz; 198 | boolean considerPrivateMethods = true; 199 | do { 200 | for (Method method : clz.getDeclaredMethods()) { 201 | if (!considerPrivateMethods && Modifier.isPrivate(method.getModifiers())) 202 | continue; 203 | 204 | if (method.getName().equals(methodName) && ClassUtils.isAssignable(parameterTypes, method.getParameterTypes(), true)) { 205 | if (bestMatch == null || MemberUtils.comparemContexteterTypes( 206 | method.getParameterTypes(), 207 | bestMatch.getParameterTypes(), 208 | parameterTypes) < 0) { 209 | bestMatch = method; 210 | } 211 | } 212 | } 213 | considerPrivateMethods = false; 214 | } while ((clz = clz.getSuperclass()) != null); 215 | 216 | if (bestMatch != null) { 217 | bestMatch.setAccessible(true); 218 | return bestMatch; 219 | } else { 220 | NoSuchMethodError e = new NoSuchMethodError(clazz.getName() + "#" + methodName); 221 | throw e; 222 | } 223 | } 224 | 225 | private static Constructor findConstructor(Class clazz, Class[] parameterTypes) { 226 | try { 227 | Constructor constructor = clazz.getDeclaredConstructor(parameterTypes); 228 | constructor.setAccessible(true); 229 | return constructor; 230 | } catch (NoSuchMethodException e) { 231 | } 232 | 233 | Constructor bestMatch = null; 234 | Constructor[] constructors = clazz.getDeclaredConstructors(); 235 | for (Constructor con : constructors) { 236 | if (ClassUtils.isAssignable(parameterTypes, con.getParameterTypes(), true)) { 237 | if (bestMatch == null || MemberUtils.comparemContexteterTypes( 238 | con.getParameterTypes(), 239 | bestMatch.getParameterTypes(), 240 | parameterTypes) < 0) { 241 | bestMatch = con; 242 | } 243 | } 244 | } 245 | if (bestMatch != null) { 246 | bestMatch.setAccessible(true); 247 | return bestMatch; 248 | } else { 249 | NoSuchMethodError e = new NoSuchMethodError(clazz.getName()); 250 | throw e; 251 | } 252 | } 253 | 254 | private static Class[] getParameterTypes(Object... args) { 255 | Class[] clazzes = new Class[args.length]; 256 | for (int i = 0; i < args.length; i++) { 257 | clazzes[i] = (args[i] != null) ? args[i].getClass() : null; 258 | } 259 | return clazzes; 260 | } 261 | 262 | public static Class[] getParameterClasses(ClassLoader classLoader, Object[] parameterTypesAndCallback) { 263 | Class[] parameterClasses = null; 264 | for (int i = parameterTypesAndCallback.length - 1; i >= 0; i--) { 265 | Object type = parameterTypesAndCallback[i]; 266 | if (type == null) 267 | throw new NullPointerException("parameter type must not be null"); 268 | 269 | // ignore trailing callback 270 | if (type instanceof MethodCallback) 271 | continue; 272 | 273 | if (parameterClasses == null) 274 | parameterClasses = new Class[i+1]; 275 | 276 | if (type instanceof Class) 277 | parameterClasses[i] = (Class) type; 278 | else if (type instanceof String) { 279 | try { 280 | parameterClasses[i] = classLoader.loadClass((String) type); //((String) type, classLoader); 281 | }catch (Exception e){ 282 | e.printStackTrace(); 283 | } 284 | } 285 | else 286 | throw new NullPointerException("parameter type must either be specified as Class or String"); 287 | } 288 | 289 | // if there are no arguments for the method 290 | if (parameterClasses == null) 291 | parameterClasses = new Class[0]; 292 | 293 | return parameterClasses; 294 | } 295 | } 296 | -------------------------------------------------------------------------------- /jhook/src/main/java/com/yanggs/jhook/utils/MemberUtils.java: -------------------------------------------------------------------------------- 1 | package com.yanggs.jhook.utils; 2 | 3 | public class MemberUtils { 4 | 5 | private static final Class[] ORDERED_PRIMITIVE_TYPES = { Byte.TYPE, Short.TYPE, 6 | Character.TYPE, Integer.TYPE, Long.TYPE, Float.TYPE, Double.TYPE }; 7 | 8 | public static int comparemContexteterTypes(Class[] left, Class[] right, Class[] actual) { 9 | float leftCost = getTotalTransformationCost(actual, left); 10 | float rightCost = getTotalTransformationCost(actual, right); 11 | return leftCost < rightCost ? -1 : rightCost < leftCost ? 1 : 0; 12 | } 13 | 14 | private static float getTotalTransformationCost(Class[] srcArgs, Class[] destArgs) { 15 | float totalCost = 0.0f; 16 | for (int i = 0; i < srcArgs.length; i++) { 17 | Class srcClass, destClass; 18 | srcClass = srcArgs[i]; 19 | destClass = destArgs[i]; 20 | totalCost += getObjectTransformationCost(srcClass, destClass); 21 | } 22 | return totalCost; 23 | } 24 | private static float getObjectTransformationCost(Class srcClass, Class destClass) { 25 | if (destClass.isPrimitive()) { 26 | return getPrimitivePromotionCost(srcClass, destClass); 27 | } 28 | float cost = 0.0f; 29 | while (srcClass != null && !destClass.equals(srcClass)) { 30 | if (destClass.isInterface() && ClassUtils.isAssignable(srcClass, destClass,true)) { 31 | cost += 0.25f; 32 | break; 33 | } 34 | cost++; 35 | srcClass = srcClass.getSuperclass(); 36 | } 37 | if (srcClass == null) { 38 | cost += 1.5f; 39 | } 40 | return cost; 41 | } 42 | 43 | private static float getPrimitivePromotionCost(final Class srcClass, final Class destClass) { 44 | float cost = 0.0f; 45 | Class cls = srcClass; 46 | if (!cls.isPrimitive()) { 47 | // slight unwrapping penalty 48 | cost += 0.1f; 49 | cls = ClassUtils.wrapperToPrimitive(cls); 50 | } 51 | for (int i = 0; cls != destClass && i < ORDERED_PRIMITIVE_TYPES.length; i++) { 52 | if (cls == ORDERED_PRIMITIVE_TYPES[i]) { 53 | cost += 0.1f; 54 | if (i < ORDERED_PRIMITIVE_TYPES.length - 1) { 55 | cls = ORDERED_PRIMITIVE_TYPES[i + 1]; 56 | } 57 | } 58 | } 59 | return cost; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /jhook/src/test/java/com/iwcode/jhook/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.yanggs.jhook; 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() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /library/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /library/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 28 5 | defaultConfig { 6 | minSdkVersion 21 7 | targetSdkVersion 28 8 | versionCode 1 9 | versionName "1.0" 10 | 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | 13 | ndk { 14 | // Specifies the ABI configurations of your native 15 | // libraries Gradle should build and package with your APK. 16 | abiFilters 'armeabi', 'armeabi-v7a', 'arm64-v8a' 17 | } 18 | externalNativeBuild { 19 | cmake { 20 | cppFlags "-std=c++11" 21 | arguments "-DANDROID_TOOLCHAIN=gcc" 22 | } 23 | } 24 | } 25 | buildTypes { 26 | release { 27 | minifyEnabled false 28 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 29 | } 30 | } 31 | externalNativeBuild { 32 | cmake { 33 | path 'src/main/cpp/CMakeLists.txt' 34 | } 35 | } 36 | } 37 | 38 | dependencies { 39 | implementation fileTree(dir: 'libs', include: ['*.jar']) 40 | } 41 | 42 | apply plugin: 'com.novoda.bintray-release' 43 | 44 | publish { 45 | userOrg = 'twsxtd'// 46 | groupId = 'me.weishu' 47 | artifactId = 'epic' 48 | publishVersion = '0.3.6' 49 | desc = 'Android Java AOP Method Hook (Dexposed on ART)' 50 | website = 'https://github.com/tiann/epic' 51 | } 52 | 53 | -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /library/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /library/src/main/cpp/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | 17 | #include 18 | #include "art.h" 19 | 20 | #define LOGV(...) ((void)__android_log_print(ANDROID_LOG_INFO, "epic.Native", __VA_ARGS__)) 21 | 22 | void* getHeap(JNIEnv* env, int api) { 23 | JavaVM* javaVM; 24 | env->GetJavaVM(&javaVM); 25 | JavaVMExt *javaVMExt = (JavaVMExt *) javaVM; 26 | 27 | void *runtime = javaVMExt->runtime; 28 | if (runtime == nullptr) { 29 | return nullptr; 30 | } 31 | 32 | if (api < 26) { 33 | Runtime_7X *runtime7X = (Runtime_7X *) runtime; 34 | return runtime7X->heap_; 35 | } else { 36 | Runtime_8X *runtime8X = (Runtime_8X *) runtime; 37 | LOGV("bootclasspath : %s", runtime8X->boot_class_path_string_.c_str()); 38 | return runtime8X->heap_; 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /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 | 71 | struct Runtime_8X { 72 | 73 | uint64_t callee_save_methods_[3]; 74 | void* pre_allocated_OutOfMemoryError_; 75 | void* pre_allocated_NoClassDefFoundError_; 76 | void* resolution_method_; 77 | void* imt_conflict_method_; 78 | // Unresolved method has the same behavior as the conflict method, it is used by the class linker 79 | // for differentiating between unfilled imt slots vs conflict slots in superclasses. 80 | void* imt_unimplemented_method_; 81 | void* sentinel_; 82 | 83 | int instruction_set_; 84 | uint32_t callee_save_method_frame_infos_[9]; // QuickMethodFrameInfo = uint32_t * 3 85 | 86 | void* compiler_callbacks_; 87 | bool is_zygote_; 88 | bool must_relocate_; 89 | bool is_concurrent_gc_enabled_; 90 | bool is_explicit_gc_disabled_; 91 | bool dex2oat_enabled_; 92 | bool image_dex2oat_enabled_; 93 | 94 | std::string compiler_executable_; 95 | std::string patchoat_executable_; 96 | std::vector compiler_options_; 97 | std::vector image_compiler_options_; 98 | std::string image_location_; 99 | 100 | std::string boot_class_path_string_; 101 | std::string class_path_string_; 102 | std::vector properties_; 103 | 104 | std::list agents_; 105 | std::vector plugins_; 106 | 107 | // The default stack size for managed threads created by the runtime. 108 | size_t default_stack_size_; 109 | 110 | void* heap_; 111 | 112 | }; 113 | 114 | struct JavaVMExt { 115 | void* functions; 116 | void* runtime; 117 | }; 118 | 119 | void *getHeap(JNIEnv*, int); 120 | 121 | #endif //EPIC_ART_H 122 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /library/src/main/cpp/fake_dlfcn.cpp: -------------------------------------------------------------------------------- 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 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include "fake_dlfcn.h" 27 | 28 | #define TAG_NAME "test2:fake_dlfcn" 29 | 30 | #define log_info(fmt,args...) __android_log_print(ANDROID_LOG_INFO, TAG_NAME, (const char *) fmt, ##args) 31 | #define log_err(fmt,args...) __android_log_print(ANDROID_LOG_ERROR, TAG_NAME, (const char *) fmt, ##args) 32 | 33 | //#ifdef LOG_DBG 34 | #define log_dbg log_info 35 | //#else 36 | //#define log_dbg(...) 37 | //#endif 38 | 39 | #ifdef __arm__ 40 | #define Elf_Ehdr Elf32_Ehdr 41 | #define Elf_Shdr Elf32_Shdr 42 | #define Elf_Sym Elf32_Sym 43 | #elif defined(__aarch64__) 44 | #define Elf_Ehdr Elf64_Ehdr 45 | #define Elf_Shdr Elf64_Shdr 46 | #define Elf_Sym Elf64_Sym 47 | #else 48 | #error "Arch unknown, please port me" 49 | #endif 50 | 51 | struct ctx { 52 | void *load_addr; 53 | void *dynstr; 54 | void *dynsym; 55 | int nsyms; 56 | off_t bias; 57 | }; 58 | 59 | extern "C" { 60 | int fake_dlclose(void *handle) { 61 | if (handle) { 62 | struct ctx *ctx = (struct ctx *) handle; 63 | if (ctx->dynsym) free(ctx->dynsym); /* we're saving dynsym and dynstr */ 64 | if (ctx->dynstr) free(ctx->dynstr); /* from library file just in case */ 65 | free(ctx); 66 | } 67 | return 0; 68 | } 69 | 70 | /* flags are ignored */ 71 | 72 | void *fake_dlopen(const char *libpath, int flags) { 73 | FILE *maps; 74 | char buff[256]; 75 | struct ctx *ctx = 0; 76 | off_t load_addr, size; 77 | int k, fd = -1, found = 0; 78 | void *shoff; 79 | Elf_Ehdr *elf = (Elf_Ehdr *) MAP_FAILED; 80 | 81 | #define fatal(fmt, args...) do { log_err(fmt,##args); goto err_exit; } while(0) 82 | 83 | maps = fopen("/proc/self/maps", "r"); 84 | if (!maps) fatal("failed to open maps"); 85 | 86 | while (!found && fgets(buff, sizeof(buff), maps)) 87 | if (strstr(buff, "r-xp") && strstr(buff, libpath)) found = 1; 88 | 89 | fclose(maps); 90 | 91 | if (!found) fatal("%s not found in my userspace", libpath); 92 | 93 | if (sscanf(buff, "%lx", &load_addr) != 1) 94 | fatal("failed to read load address for %s", libpath); 95 | 96 | log_info("%s loaded in Android at 0x%08lx", libpath, load_addr); 97 | 98 | /* Now, mmap the same library once again */ 99 | 100 | fd = open(libpath, O_RDONLY); 101 | if (fd < 0) fatal("failed to open %s", libpath); 102 | 103 | size = lseek(fd, 0, SEEK_END); 104 | if (size <= 0) fatal("lseek() failed for %s", libpath); 105 | 106 | elf = (Elf_Ehdr *) mmap(0, size, PROT_READ, MAP_SHARED, fd, 0); 107 | close(fd); 108 | fd = -1; 109 | 110 | if (elf == MAP_FAILED) fatal("mmap() failed for %s", libpath); 111 | 112 | ctx = (struct ctx *) calloc(1, sizeof(struct ctx)); 113 | if (!ctx) fatal("no memory for %s", libpath); 114 | 115 | ctx->load_addr = (void *) load_addr; 116 | shoff = ((void *) elf) + elf->e_shoff; 117 | 118 | for (k = 0; k < elf->e_shnum; k++, shoff += elf->e_shentsize) { 119 | 120 | Elf_Shdr *sh = (Elf_Shdr *) shoff; 121 | log_dbg("%s: k=%d shdr=%p type=%x", __func__, k, sh, sh->sh_type); 122 | 123 | switch (sh->sh_type) { 124 | 125 | case SHT_DYNSYM: 126 | if (ctx->dynsym) fatal("%s: duplicate DYNSYM sections", libpath); /* .dynsym */ 127 | ctx->dynsym = malloc(sh->sh_size); 128 | if (!ctx->dynsym) fatal("%s: no memory for .dynsym", libpath); 129 | memcpy(ctx->dynsym, ((void *) elf) + sh->sh_offset, sh->sh_size); 130 | ctx->nsyms = (sh->sh_size / sizeof(Elf_Sym)); 131 | break; 132 | 133 | case SHT_STRTAB: 134 | if (ctx->dynstr) break; /* .dynstr is guaranteed to be the first STRTAB */ 135 | ctx->dynstr = malloc(sh->sh_size); 136 | if (!ctx->dynstr) fatal("%s: no memory for .dynstr", libpath); 137 | memcpy(ctx->dynstr, ((void *) elf) + sh->sh_offset, sh->sh_size); 138 | break; 139 | 140 | case SHT_PROGBITS: 141 | if (!ctx->dynstr || !ctx->dynsym) break; 142 | /* won't even bother checking against the section name */ 143 | ctx->bias = (off_t) sh->sh_addr - (off_t) sh->sh_offset; 144 | k = elf->e_shnum; /* exit for */ 145 | break; 146 | } 147 | } 148 | 149 | munmap(elf, size); 150 | elf = 0; 151 | 152 | if (!ctx->dynstr || !ctx->dynsym) fatal("dynamic sections not found in %s", libpath); 153 | 154 | #undef fatal 155 | 156 | log_dbg("%s: ok, dynsym = %p, dynstr = %p", libpath, ctx->dynsym, ctx->dynstr); 157 | 158 | return ctx; 159 | 160 | err_exit: 161 | if (fd >= 0) close(fd); 162 | if (elf != MAP_FAILED) munmap(elf, size); 163 | fake_dlclose(ctx); 164 | return 0; 165 | } 166 | 167 | void *fake_dlsym(void *handle, const char *name) { 168 | int k; 169 | struct ctx *ctx = (struct ctx *) handle; 170 | Elf_Sym *sym = (Elf_Sym *) ctx->dynsym; 171 | char *strings = (char *) ctx->dynstr; 172 | 173 | for (k = 0; k < ctx->nsyms; k++, sym++) 174 | if (strcmp(strings + sym->st_name, name) == 0) { 175 | /* NB: sym->st_value is an offset into the section for relocatables, 176 | but a VMA for shared libs or exe files, so we have to subtract the bias */ 177 | void *ret = ctx->load_addr + sym->st_value - ctx->bias; 178 | log_info("%s found at %p", name, ret); 179 | return ret; 180 | } 181 | return 0; 182 | } 183 | } 184 | 185 | 186 | -------------------------------------------------------------------------------- /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 *fake_dlopen(const char *libpath, int flags); 31 | void *fake_dlsym(void *handle, const char *name); 32 | 33 | }; 34 | #endif //DEXPOSED_DLFCN_H 35 | -------------------------------------------------------------------------------- /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/XC_MethodHook.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 java.lang.reflect.Member; 22 | 23 | import com.taobao.android.dexposed.callbacks.IXUnhook; 24 | import com.taobao.android.dexposed.callbacks.XCallback; 25 | 26 | public abstract class XC_MethodHook extends XCallback { 27 | public XC_MethodHook() { 28 | super(); 29 | } 30 | public XC_MethodHook(int priority) { 31 | super(priority); 32 | } 33 | 34 | /** 35 | * Called before the invocation of the method. 36 | *

Can use {@link MethodHookParam#setResult(Object)} and {@link MethodHookParam#setThrowable(Throwable)} 37 | * to prevent the original method from being called. 38 | */ 39 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {} 40 | 41 | /** 42 | * Called after the invocation of the method. 43 | *

Can use {@link MethodHookParam#setResult(Object)} and {@link MethodHookParam#setThrowable(Throwable)} 44 | * to modify the return value of the original method. 45 | */ 46 | protected void afterHookedMethod(MethodHookParam param) throws Throwable {} 47 | 48 | 49 | public static class MethodHookParam extends Param { 50 | /** Description of the hooked method */ 51 | public Member method; 52 | /** The this reference for an instance method, or null for static methods */ 53 | public Object thisObject; 54 | /** Arguments to the method call */ 55 | public Object[] args; 56 | 57 | private Object result = null; 58 | private Throwable throwable = null; 59 | /* package */ boolean returnEarly = false; 60 | 61 | /** Returns the result of the method call */ 62 | public Object getResult() { 63 | return result; 64 | } 65 | 66 | /** 67 | * Modify the result of the method call. In a "before-method-call" 68 | * hook, prevents the call to the original method. 69 | * You still need to "return" from the hook handler if required. 70 | */ 71 | public void setResult(Object result) { 72 | this.result = result; 73 | this.throwable = null; 74 | this.returnEarly = true; 75 | } 76 | 77 | /** Returns the Throwable thrown by the method, or null */ 78 | public Throwable getThrowable() { 79 | return throwable; 80 | } 81 | 82 | /** Returns true if an exception was thrown by the method */ 83 | public boolean hasThrowable() { 84 | return throwable != null; 85 | } 86 | 87 | /** 88 | * Modify the exception thrown of the method call. In a "before-method-call" 89 | * hook, prevents the call to the original method. 90 | * You still need to "return" from the hook handler if required. 91 | */ 92 | public void setThrowable(Throwable throwable) { 93 | this.throwable = throwable; 94 | this.result = null; 95 | this.returnEarly = true; 96 | } 97 | 98 | /** Returns the result of the method call, or throws the Throwable caused by it */ 99 | public Object getResultOrThrowable() throws Throwable { 100 | if (throwable != null) 101 | throw throwable; 102 | return result; 103 | } 104 | } 105 | 106 | public class Unhook implements IXUnhook { 107 | private final Member hookMethod; 108 | 109 | public Unhook(Member hookMethod) { 110 | this.hookMethod = hookMethod; 111 | } 112 | 113 | public Member getHookedMethod() { 114 | return hookMethod; 115 | } 116 | 117 | public XC_MethodHook getCallback() { 118 | return XC_MethodHook.this; 119 | } 120 | 121 | @Override 122 | public void unhook() { 123 | DexposedBridge.unhookMethod(hookMethod, XC_MethodHook.this); 124 | } 125 | 126 | } 127 | 128 | /** 129 | * 130 | * Note: This class used for distinguish the Method cann't unhook. 131 | * 132 | */ 133 | public abstract class XC_MethodKeepHook extends XC_MethodHook { 134 | 135 | public XC_MethodKeepHook() { 136 | super(); 137 | } 138 | public XC_MethodKeepHook(int priority) { 139 | super(priority); 140 | } 141 | 142 | } 143 | 144 | 145 | } 146 | 147 | 148 | -------------------------------------------------------------------------------- /library/src/main/java/com/taobao/android/dexposed/XC_MethodReplacement.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 | 22 | public abstract class XC_MethodReplacement extends XC_MethodHook { 23 | public XC_MethodReplacement() { 24 | super(); 25 | } 26 | public XC_MethodReplacement(int priority) { 27 | super(priority); 28 | } 29 | 30 | @Override 31 | protected final void beforeHookedMethod(MethodHookParam param) throws Throwable { 32 | try { 33 | Object result = replaceHookedMethod(param); 34 | param.setResult(result); 35 | } catch (Throwable t) { 36 | param.setThrowable(t); 37 | } 38 | } 39 | 40 | protected final void afterHookedMethod(MethodHookParam param) throws Throwable {} 41 | 42 | /** 43 | * Shortcut for replacing a method completely. Whatever is returned/thrown here is taken 44 | * instead of the result of the original method (which will not be called). 45 | */ 46 | protected abstract Object replaceHookedMethod(MethodHookParam param) throws Throwable; 47 | 48 | public static final XC_MethodReplacement DO_NOTHING = new XC_MethodReplacement(PRIORITY_HIGHEST*2) { 49 | @Override 50 | protected Object replaceHookedMethod(MethodHookParam param) throws Throwable { 51 | return null; 52 | }; 53 | }; 54 | 55 | /** 56 | * Creates a callback which always returns a specific value 57 | */ 58 | public static XC_MethodReplacement returnConstant(final Object result) { 59 | return returnConstant(PRIORITY_DEFAULT, result); 60 | } 61 | 62 | /** 63 | * @see #returnConstant(Object) 64 | */ 65 | public static XC_MethodReplacement returnConstant(int priority, final Object result) { 66 | return new XC_MethodReplacement(priority) { 67 | @Override 68 | protected Object replaceHookedMethod(MethodHookParam param) throws Throwable { 69 | return result; 70 | } 71 | }; 72 | } 73 | 74 | 75 | /** 76 | * 77 | * Note: This class used for distinguish the Method cann't unhook. 78 | * 79 | */ 80 | public abstract class XC_MethodKeepReplacement extends XC_MethodReplacement { 81 | public XC_MethodKeepReplacement() { 82 | super(); 83 | } 84 | public XC_MethodKeepReplacement(int priority) { 85 | super(priority); 86 | } 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /library/src/main/java/com/taobao/android/dexposed/callbacks/IXUnhook.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.callbacks; 20 | 21 | public interface IXUnhook { 22 | void unhook(); 23 | } 24 | -------------------------------------------------------------------------------- /library/src/main/java/com/taobao/android/dexposed/callbacks/XCallback.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.callbacks; 20 | 21 | import java.io.Serializable; 22 | 23 | import com.taobao.android.dexposed.DexposedBridge; 24 | import com.taobao.android.dexposed.DexposedBridge.CopyOnWriteSortedSet; 25 | 26 | import android.os.Bundle; 27 | 28 | public abstract class XCallback implements Comparable { 29 | public final int priority; 30 | public XCallback() { 31 | this.priority = PRIORITY_DEFAULT; 32 | } 33 | public XCallback(int priority) { 34 | this.priority = priority; 35 | } 36 | 37 | public static class Param { 38 | public final Object[] callbacks; 39 | private Bundle extra; 40 | 41 | protected Param() { 42 | callbacks = null; 43 | } 44 | 45 | protected Param(CopyOnWriteSortedSet callbacks) { 46 | this.callbacks = callbacks.getSnapshot(); 47 | } 48 | 49 | /** 50 | * This can be used to store anything for the scope of the callback. 51 | * Use this instead of instance variables. 52 | * @see #getObjectExtra 53 | * @see #setObjectExtra 54 | */ 55 | public synchronized Bundle getExtra() { 56 | if (extra == null) 57 | extra = new Bundle(); 58 | return extra; 59 | } 60 | 61 | /** @see #setObjectExtra */ 62 | public Object getObjectExtra(String key) { 63 | Serializable o = getExtra().getSerializable(key); 64 | if (o instanceof SerializeWrapper) 65 | return ((SerializeWrapper) o).object; 66 | return null; 67 | } 68 | 69 | /** Provides a wrapper to store Objects in extra. */ 70 | public void setObjectExtra(String key, Object o) { 71 | getExtra().putSerializable(key, new SerializeWrapper(o)); 72 | } 73 | 74 | private static class SerializeWrapper implements Serializable { 75 | private static final long serialVersionUID = 1L; 76 | private Object object; 77 | public SerializeWrapper(Object o) { 78 | object = o; 79 | } 80 | } 81 | } 82 | 83 | public static final void callAll(Param param) { 84 | if (param.callbacks == null) 85 | throw new IllegalStateException("This object was not created for use with callAll"); 86 | 87 | for (int i = 0; i < param.callbacks.length; i++) { 88 | try { 89 | ((XCallback) param.callbacks[i]).call(param); 90 | } catch (Throwable t) { DexposedBridge.log(t); } 91 | } 92 | } 93 | 94 | protected void call(Param param) throws Throwable {}; 95 | 96 | @Override 97 | public int compareTo(XCallback other) { 98 | if (this == other) 99 | return 0; 100 | 101 | // order descending by priority 102 | if (other.priority != this.priority) 103 | return other.priority - this.priority; 104 | // then randomly 105 | else if (System.identityHashCode(this) < System.identityHashCode(other)) 106 | return -1; 107 | else 108 | return 1; 109 | } 110 | 111 | public static final int PRIORITY_DEFAULT = 50; 112 | /** Call this handler last */ 113 | public static final int PRIORITY_LOWEST = -10000; 114 | /** Call this handler first */ 115 | public static final int PRIORITY_HIGHEST = 10000; 116 | } 117 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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/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_2; 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 | if (isArm) { 57 | if (Runtime.is64Bit()) { 58 | switch (apiLevel) { 59 | case Build.VERSION_CODES.M: 60 | ShellCode = new Arm64_2(); 61 | break; 62 | case Build.VERSION_CODES.N: 63 | case Build.VERSION_CODES.N_MR1: 64 | case Build.VERSION_CODES.O: 65 | case Build.VERSION_CODES.O_MR1: 66 | ShellCode = new Arm64_2(); 67 | break; 68 | case Build.VERSION_CODES.P: 69 | ShellCode = new Arm64_2(); 70 | break; 71 | } 72 | } else if (Runtime.isThumb2()) { 73 | ShellCode = new Thumb2(); 74 | } else { 75 | // todo ARM32 76 | Logger.w(TAG, "ARM32, not support now."); 77 | } 78 | } 79 | if (ShellCode == null) { 80 | throw new RuntimeException("Do not support this ARCH now!! API LEVEL:" + apiLevel); 81 | } 82 | Logger.i(TAG, "Using: " + ShellCode.getName()); 83 | } 84 | 85 | public static boolean hookMethod(Constructor origin) { 86 | return hookMethod(ArtMethod.of(origin)); 87 | } 88 | 89 | public static boolean hookMethod(Method origin) { 90 | ArtMethod artOrigin = ArtMethod.of(origin); 91 | return hookMethod(artOrigin); 92 | } 93 | 94 | private static boolean hookMethod(ArtMethod artOrigin) { 95 | 96 | MethodInfo methodInfo = new MethodInfo(); 97 | methodInfo.isStatic = Modifier.isStatic(artOrigin.getModifiers()); 98 | final Class[] parameterTypes = artOrigin.getParameterTypes(); 99 | if (parameterTypes != null) { 100 | methodInfo.paramNumber = parameterTypes.length; 101 | methodInfo.paramTypes = parameterTypes; 102 | } else { 103 | methodInfo.paramNumber = 0; 104 | methodInfo.paramTypes = new Class[0]; 105 | } 106 | methodInfo.returnType = artOrigin.getReturnType(); 107 | methodInfo.method = artOrigin; 108 | originSigs.put(artOrigin.getAddress(), methodInfo); 109 | 110 | if (!artOrigin.isAccessible()) { 111 | artOrigin.setAccessible(true); 112 | } 113 | 114 | artOrigin.ensureResolved(); 115 | 116 | long originEntry = artOrigin.getEntryPointFromQuickCompiledCode(); 117 | if (originEntry == ArtMethod.getQuickToInterpreterBridge()) { 118 | Logger.i(TAG, "this method is not compiled, compile it now. current entry: 0x" + Long.toHexString(originEntry)); 119 | boolean ret = artOrigin.compile(); 120 | if (ret) { 121 | originEntry = artOrigin.getEntryPointFromQuickCompiledCode(); 122 | Logger.i(TAG, "compile method success, new entry: 0x" + Long.toHexString(originEntry)); 123 | } else { 124 | Logger.e(TAG, "compile method failed..."); 125 | return false; 126 | // return hookInterpreterBridge(artOrigin); 127 | } 128 | } 129 | 130 | ArtMethod backupMethod = artOrigin.backup(); 131 | 132 | Logger.i(TAG, "backup method address:" + Debug.addrHex(backupMethod.getAddress())); 133 | Logger.i(TAG, "backup method entry :" + Debug.addrHex(backupMethod.getEntryPointFromQuickCompiledCode())); 134 | 135 | ArtMethod backupList = getBackMethod(artOrigin); 136 | if (backupList == null) { 137 | setBackMethod(artOrigin, backupMethod); 138 | } 139 | 140 | final long key = originEntry; 141 | final EntryLock lock = EntryLock.obtain(originEntry); 142 | //noinspection SynchronizationOnLocalVariableOrMethodParameter 143 | synchronized (lock) { 144 | if (!scripts.containsKey(key)) { 145 | scripts.put(key, new Trampoline(ShellCode, originEntry)); 146 | } 147 | Trampoline trampoline = scripts.get(key); 148 | boolean ret = trampoline.install(artOrigin); 149 | // Logger.d(TAG, "hook Method result:" + ret); 150 | return ret; 151 | } 152 | } 153 | 154 | /* 155 | private static boolean hookInterpreterBridge(ArtMethod artOrigin) { 156 | 157 | String identifier = artOrigin.getIdentifier(); 158 | ArtMethod backupMethod = artOrigin.backup(); 159 | 160 | Logger.d(TAG, "backup method address:" + Debug.addrHex(backupMethod.getAddress())); 161 | Logger.d(TAG, "backup method entry :" + Debug.addrHex(backupMethod.getEntryPointFromQuickCompiledCode())); 162 | 163 | List backupList = backupMethodsMapping.get(identifier); 164 | if (backupList == null) { 165 | backupList = new LinkedList(); 166 | backupMethodsMapping.put(identifier, backupList); 167 | } 168 | backupList.add(backupMethod); 169 | 170 | long originalEntryPoint = ShellCode.toMem(artOrigin.getEntryPointFromQuickCompiledCode()); 171 | Logger.d(TAG, "originEntry Point(bridge):" + Debug.addrHex(originalEntryPoint)); 172 | 173 | originalEntryPoint += 16; 174 | Logger.d(TAG, "originEntry Point(offset8):" + Debug.addrHex(originalEntryPoint)); 175 | 176 | if (!scripts.containsKey(originalEntryPoint)) { 177 | scripts.put(originalEntryPoint, new Trampoline(ShellCode, artOrigin)); 178 | } 179 | Trampoline trampoline = scripts.get(originalEntryPoint); 180 | 181 | boolean ret = trampoline.install(); 182 | Logger.i(TAG, "hook Method result:" + ret); 183 | return ret; 184 | 185 | }*/ 186 | 187 | public synchronized static ArtMethod getBackMethod(ArtMethod origin) { 188 | String identifier = origin.getIdentifier(); 189 | return backupMethodsMapping.get(identifier); 190 | } 191 | 192 | public static synchronized void setBackMethod(ArtMethod origin, ArtMethod backup) { 193 | String identifier = origin.getIdentifier(); 194 | backupMethodsMapping.put(identifier, backup); 195 | } 196 | 197 | public static MethodInfo getMethodInfo(long address) { 198 | return originSigs.get(address); 199 | } 200 | 201 | public static int getQuickCompiledCodeSize(ArtMethod method) { 202 | 203 | long entryPoint = ShellCode.toMem(method.getEntryPointFromQuickCompiledCode()); 204 | long sizeInfo1 = entryPoint - 4; 205 | byte[] bytes = EpicNative.get(sizeInfo1, 4); 206 | int size = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getInt(); 207 | Logger.d(TAG, "getQuickCompiledCodeSize: " + size); 208 | return size; 209 | } 210 | 211 | 212 | public static class MethodInfo { 213 | public boolean isStatic; 214 | public int paramNumber; 215 | public Class[] paramTypes; 216 | public Class returnType; 217 | public ArtMethod method; 218 | 219 | @Override 220 | public String toString() { 221 | return method.toGenericString(); 222 | } 223 | } 224 | 225 | private static class EntryLock { 226 | static Map sLockPool = new HashMap<>(); 227 | 228 | static synchronized EntryLock obtain(long entry) { 229 | if (sLockPool.containsKey(entry)) { 230 | return sLockPool.get(entry); 231 | } else { 232 | EntryLock entryLock = new EntryLock(); 233 | sLockPool.put(entry, entryLock); 234 | return entryLock; 235 | } 236 | } 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /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.XposedHelpers; 23 | import com.taobao.android.dexposed.utility.Debug; 24 | import com.taobao.android.dexposed.utility.Logger; 25 | import com.taobao.android.dexposed.utility.Unsafe; 26 | 27 | import java.lang.reflect.Member; 28 | 29 | import static com.taobao.android.dexposed.utility.Debug.addrHex; 30 | 31 | 32 | public final class EpicNative { 33 | 34 | private static final String TAG = "EpicNative"; 35 | private static volatile boolean useUnsafe = false; 36 | static { 37 | try { 38 | System.loadLibrary("epic"); 39 | useUnsafe = DeviceCheck.isYunOS() || !isGetObjectAvailable(); 40 | Log.i(TAG, "use unsafe ? " + useUnsafe); 41 | } catch (Throwable e) { 42 | Log.e(TAG, "init EpicNative error", e); 43 | } 44 | } 45 | 46 | public static native long mmap(int length); 47 | 48 | public static native boolean munmap(long address, int length); 49 | 50 | public static native void memcpy(long src, long dest, int length); 51 | 52 | public static native void memput(byte[] bytes, long dest); 53 | 54 | public static native byte[] memget(long src, int length); 55 | 56 | public static native boolean munprotect(long addr, long len); 57 | 58 | public static native long getMethodAddress(Member method); 59 | 60 | public static native boolean cacheflush(long addr, long len); 61 | 62 | public static native long malloc(int sizeOfPtr); 63 | 64 | public static native Object getObjectNative(long self, long address); 65 | 66 | private static native boolean isGetObjectAvailable(); 67 | 68 | public static Object getObject(long self, long address) { 69 | if (useUnsafe) { 70 | return Unsafe.getObject(address); 71 | } else { 72 | return getObjectNative(self, address); 73 | } 74 | } 75 | 76 | public static native boolean compileMethod(Member method, long self); 77 | 78 | /** 79 | * suspend all running thread momently 80 | * @return a handle to resume all thread, used by {@link #resumeAll(long)} 81 | */ 82 | public static native long suspendAll(); 83 | 84 | /** 85 | * resume all thread which are suspend by {@link #suspendAll()} 86 | * only work abobe Android N 87 | * @param cookie the cookie return by {@link #suspendAll()} 88 | */ 89 | public static native void resumeAll(long cookie); 90 | 91 | /** 92 | * stop jit compiler in runtime. 93 | * Warning: Just for experiment Do not call this now!!! 94 | * @return cookie use by {@link #startJit(long)} 95 | */ 96 | public static native long stopJit(); 97 | 98 | /** 99 | * start jit compiler stop by {@link #stopJit()} 100 | * Warning: Just for experiment Do not call this now!!! 101 | * @param cookie the cookie return by {@link #stopJit()} 102 | */ 103 | public static native void startJit(long cookie); 104 | 105 | // FIXME: 17/12/29 reimplement it with pure native code. 106 | static native boolean activateNative(long jumpToAddress, long pc, long sizeOfTargetJump, long sizeOfBridgeJump, byte[] code); 107 | 108 | /** 109 | * Disable the moving gc of runtime. 110 | * Warning: Just for experiment Do not call this now!!! 111 | * @param api the api level 112 | */ 113 | public static native void disableMovingGc(int api); 114 | 115 | 116 | private EpicNative() { 117 | } 118 | 119 | public static boolean compileMethod(Member method) { 120 | final long nativePeer = XposedHelpers.getLongField(Thread.currentThread(), "nativePeer"); 121 | return compileMethod(method, nativePeer); 122 | } 123 | 124 | public static Object getObject(long address) { 125 | final long nativePeer = XposedHelpers.getLongField(Thread.currentThread(), "nativePeer"); 126 | return getObject(nativePeer, address); 127 | } 128 | 129 | public static long map(int length) { 130 | long m = mmap(length); 131 | Logger.i(TAG, "Mapped memory of size " + length + " at " + addrHex(m)); 132 | return m; 133 | } 134 | 135 | public static boolean unmap(long address, int length) { 136 | Logger.d(TAG, "Removing mapped memory of size " + length + " at " + addrHex(address)); 137 | return munmap(address, length); 138 | } 139 | 140 | public static void put(byte[] bytes, long dest) { 141 | if (Debug.DEBUG) { 142 | Logger.d(TAG, "Writing memory to: " + addrHex(dest)); 143 | Logger.d(TAG, Debug.hexdump(bytes, dest)); 144 | } 145 | memput(bytes, dest); 146 | } 147 | 148 | public static byte[] get(long src, int length) { 149 | Logger.d(TAG, "Reading " + length + " bytes from: " + addrHex(src)); 150 | byte[] bytes = memget(src, length); 151 | Logger.d(TAG, Debug.hexdump(bytes, src)); 152 | return bytes; 153 | } 154 | 155 | public static boolean unprotect(long addr, long len) { 156 | Logger.d(TAG, "Disabling mprotect from " + addrHex(addr)); 157 | return munprotect(addr, len); 158 | } 159 | 160 | public static void copy(long src, long dst, int length) { 161 | Logger.d(TAG, "Copy " + length + " bytes form " + addrHex(src) + " to " + addrHex(dst)); 162 | memcpy(src, dst, length); 163 | } 164 | 165 | } 166 | 167 | 168 | 169 | -------------------------------------------------------------------------------- /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_2; 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_2.getBridgeMethod(methodInfo) 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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(), sizeOfDirectJump()); 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 | -------------------------------------------------------------------------------- /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 8; 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/entry/Entry64.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.entry; 18 | 19 | import com.taobao.android.dexposed.DexposedBridge; 20 | import com.taobao.android.dexposed.XposedHelpers; 21 | import com.taobao.android.dexposed.utility.Logger; 22 | 23 | import java.lang.reflect.Method; 24 | import java.nio.ByteBuffer; 25 | import java.nio.ByteOrder; 26 | import java.util.Arrays; 27 | import java.util.HashMap; 28 | import java.util.Map; 29 | 30 | import me.weishu.epic.art.Epic; 31 | import me.weishu.epic.art.EpicNative; 32 | 33 | @SuppressWarnings({"unused", "ConstantConditions"}) 34 | public class Entry64 { 35 | 36 | private final static String TAG = "Entry64"; 37 | 38 | //region ---------------callback--------------- 39 | private static int onHookInt(Object artmethod, Object receiver, Object[] args) { 40 | return (Integer) DexposedBridge.handleHookedArtMethod(artmethod, receiver, args); 41 | } 42 | 43 | private static long onHookLong(Object artmethod, Object receiver, Object[] args) { 44 | return (Long) DexposedBridge.handleHookedArtMethod(artmethod, receiver, args); 45 | } 46 | 47 | private static double onHookDouble(Object artmethod, Object receiver, Object[] args) { 48 | return (Double) DexposedBridge.handleHookedArtMethod(artmethod, receiver, args); 49 | } 50 | 51 | private static char onHookChar(Object artmethod, Object receiver, Object[] args) { 52 | return (Character) DexposedBridge.handleHookedArtMethod(artmethod, receiver, args); 53 | } 54 | 55 | private static short onHookShort(Object artmethod, Object receiver, Object[] args) { 56 | return (Short) DexposedBridge.handleHookedArtMethod(artmethod, receiver, args); 57 | } 58 | 59 | private static float onHookFloat(Object artmethod, Object receiver, Object[] args) { 60 | return (Float) DexposedBridge.handleHookedArtMethod(artmethod, receiver, args); 61 | } 62 | 63 | private static Object onHookObject(Object artmethod, Object receiver, Object[] args) { 64 | return DexposedBridge.handleHookedArtMethod(artmethod, receiver, args); 65 | } 66 | 67 | private static void onHookVoid(Object artmethod, Object receiver, Object[] args) { 68 | DexposedBridge.handleHookedArtMethod(artmethod, receiver, args); 69 | } 70 | 71 | private static boolean onHookBoolean(Object artmethod, Object receiver, Object[] args) { 72 | return (Boolean) DexposedBridge.handleHookedArtMethod(artmethod, receiver, args); 73 | } 74 | 75 | private static byte onHookByte(Object artmethod, Object receiver, Object[] args) { 76 | return (Byte) DexposedBridge.handleHookedArtMethod(artmethod, receiver, args); 77 | } 78 | //endregion 79 | 80 | //region ---------------bridge--------------- 81 | private static void voidBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) { 82 | referenceBridge(r1, self, struct, x4, x5, x6, x7); 83 | } 84 | 85 | private static boolean booleanBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) { 86 | return (Boolean) referenceBridge(r1, self, struct, x4, x5, x6, x7); 87 | } 88 | 89 | private static byte byteBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) { 90 | return (Byte) referenceBridge(r1, self, struct, x4, x5, x6, x7); 91 | } 92 | 93 | private static short shortBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) { 94 | return (Short) referenceBridge(r1, self, struct, x4, x5, x6, x7); 95 | } 96 | 97 | private static char charBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) { 98 | return (Character) referenceBridge(r1, self, struct, x4, x5, x6, x7); 99 | } 100 | 101 | private static int intBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) { 102 | return (Integer) referenceBridge(r1, self, struct, x4, x5, x6, x7); 103 | } 104 | 105 | private static long longBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) { 106 | return (Long) referenceBridge(r1, self, struct, x4, x5, x6, x7); 107 | } 108 | 109 | private static float floatBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) { 110 | return (Float) referenceBridge(r1, self, struct, x4, x5, x6, x7); 111 | } 112 | 113 | private static double doubleBridge(long r1, long self, long struct, long x4, long x5, long x6, long x7) { 114 | return (Double) referenceBridge(r1, self, struct, x4, x5, x6, x7); 115 | } 116 | //endregion 117 | 118 | private static Object referenceBridge(long x1, long self, long struct, long x4, long x5, long x6, long x7) { 119 | Logger.i(TAG, "enter bridge function."); 120 | 121 | // struct { 122 | // void* sp; 123 | // void* r2; 124 | // void* r3; 125 | // void* sourceMethod 126 | // } 127 | // sp + 16 = r4 128 | 129 | Logger.d(TAG, "self:" + Long.toHexString(self)); 130 | final long nativePeer = XposedHelpers.getLongField(Thread.currentThread(), "nativePeer"); 131 | Logger.d(TAG, "java thread native peer:" + Long.toHexString(nativePeer)); 132 | 133 | Logger.d(TAG, "struct:" + Long.toHexString(struct)); 134 | 135 | final long sp = ByteBuffer.wrap(EpicNative.get(struct, 8)).order(ByteOrder.LITTLE_ENDIAN).getLong(); 136 | 137 | Logger.d(TAG, "stack:" + sp); 138 | 139 | final byte[] rr1 = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(x1).array(); 140 | final byte[] r2 = EpicNative.get(struct + 8, 8); 141 | 142 | final byte[] r3 = EpicNative.get(struct + 16, 8); 143 | 144 | final long sourceMethod = ByteBuffer.wrap(EpicNative.get(struct + 24, 8)).order(ByteOrder.LITTLE_ENDIAN).getLong(); 145 | Logger.d(TAG, "sourceMethod:" + Long.toHexString(sourceMethod)); 146 | 147 | Epic.MethodInfo originMethodInfo = Epic.getMethodInfo(sourceMethod); 148 | Logger.d(TAG, "originMethodInfo :" + originMethodInfo); 149 | 150 | boolean isStatic = originMethodInfo.isStatic; 151 | 152 | int numberOfArgs = originMethodInfo.paramNumber; 153 | Class[] typeOfArgs = originMethodInfo.paramTypes; 154 | Object[] arguments = new Object[numberOfArgs]; 155 | 156 | Object receiver; 157 | 158 | self = nativePeer; 159 | 160 | if (isStatic) { 161 | receiver = null; 162 | do { 163 | if (numberOfArgs == 0) break; 164 | arguments[0] = wrapArgument(typeOfArgs[0], self, rr1); 165 | if (numberOfArgs == 1) break; 166 | arguments[1] = wrapArgument(typeOfArgs[1], self, r2); 167 | if (numberOfArgs == 2) break; 168 | arguments[2] = wrapArgument(typeOfArgs[2], self, r3); 169 | if (numberOfArgs == 3) break; 170 | arguments[3] = wrapArgument(typeOfArgs[3], self, x4); 171 | if (numberOfArgs == 4) break; 172 | arguments[4] = wrapArgument(typeOfArgs[4], self, x5); 173 | if (numberOfArgs == 5) break; 174 | arguments[5] = wrapArgument(typeOfArgs[5], self, x6); 175 | if (numberOfArgs == 6) break; 176 | arguments[6] = wrapArgument(typeOfArgs[6], self, x7); 177 | if (numberOfArgs == 7) break; 178 | 179 | for (int i = 7; i < numberOfArgs; i++) { 180 | byte[] argsInStack = EpicNative.get(sp + i * 8 + 8, 8); 181 | arguments[i] = wrapArgument(typeOfArgs[i], self, argsInStack); 182 | } 183 | } while (false); 184 | 185 | } else { 186 | 187 | receiver = EpicNative.getObject(self, x1); 188 | Logger.i(TAG, "this :" + receiver); 189 | 190 | do { 191 | if (numberOfArgs == 0) break; 192 | arguments[0] = wrapArgument(typeOfArgs[0], self, r2); 193 | if (numberOfArgs == 1) break; 194 | arguments[1] = wrapArgument(typeOfArgs[1], self, r3); 195 | if (numberOfArgs == 2) break; 196 | arguments[2] = wrapArgument(typeOfArgs[2], self, x4); 197 | if (numberOfArgs == 3) break; 198 | arguments[3] = wrapArgument(typeOfArgs[3], self, x5); 199 | if (numberOfArgs == 4) break; 200 | arguments[4] = wrapArgument(typeOfArgs[4], self, x6); 201 | if (numberOfArgs == 5) break; 202 | arguments[5] = wrapArgument(typeOfArgs[5], self, x7); 203 | if (numberOfArgs == 6) break; 204 | 205 | for (int i = 6; i < numberOfArgs; i++) { 206 | byte[] argsInStack = EpicNative.get(sp + i * 8 + 16, 8); 207 | arguments[i] = wrapArgument(typeOfArgs[i], self, argsInStack); 208 | } 209 | } while (false); 210 | } 211 | 212 | Logger.i(TAG, "arguments:" + Arrays.toString(arguments)); 213 | 214 | Class returnType = originMethodInfo.returnType; 215 | Object artMethod = originMethodInfo.method; 216 | 217 | Logger.d(TAG, "leave bridge function"); 218 | 219 | if (returnType == void.class) { 220 | onHookVoid(artMethod, receiver, arguments); 221 | return 0; 222 | } else if (returnType == char.class) { 223 | return onHookChar(artMethod, receiver, arguments); 224 | } else if (returnType == byte.class) { 225 | return onHookByte(artMethod, receiver, arguments); 226 | } else if (returnType == short.class) { 227 | return onHookShort(artMethod, receiver, arguments); 228 | } else if (returnType == int.class) { 229 | return onHookInt(artMethod, receiver, arguments); 230 | } else if (returnType == long.class) { 231 | return onHookLong(artMethod, receiver, arguments); 232 | } else if (returnType == float.class) { 233 | return onHookFloat(artMethod, receiver, arguments); 234 | } else if (returnType == double.class) { 235 | return onHookDouble(artMethod, receiver, arguments); 236 | } else if (returnType == boolean.class) { 237 | return onHookBoolean(artMethod, receiver, arguments); 238 | } else { 239 | return onHookObject(artMethod, receiver, arguments); 240 | } 241 | } 242 | 243 | private static Object wrapArgument(Class type, long self, long value) { 244 | return wrapArgument(type, self, ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(value).array()); 245 | } 246 | 247 | private static Object wrapArgument(Class type, long self, byte[] value) { 248 | final ByteBuffer byteBuffer = ByteBuffer.wrap(value).order(ByteOrder.LITTLE_ENDIAN); 249 | if (type.isPrimitive()) { 250 | if (type == int.class) { 251 | return byteBuffer.getInt(); 252 | } else if (type == long.class) { 253 | return byteBuffer.getLong(); 254 | } else if (type == float.class) { 255 | return byteBuffer.getFloat(); 256 | } else if (type == short.class) { 257 | return byteBuffer.getShort(); 258 | } else if (type == byte.class) { 259 | return byteBuffer.get(); 260 | } else if (type == char.class) { 261 | return byteBuffer.getChar(); 262 | } else if (type == double.class) { 263 | return byteBuffer.getDouble(); 264 | } else if (type == boolean.class) { 265 | return byteBuffer.getInt() == 0; 266 | } else { 267 | throw new RuntimeException("unknown type:" + type); 268 | } 269 | } else { 270 | long address = byteBuffer.getLong(); 271 | Object object = EpicNative.getObject(self, address); 272 | //Logger.i(TAG, "wrapArgument, address: 0x" + Long.toHexString(address) + ", value:" + object); 273 | return object; 274 | } 275 | } 276 | 277 | private static Map, String> bridgeMethodMap = new HashMap<>(); 278 | static { 279 | Class[] primitiveTypes = new Class[]{boolean.class, byte.class, char.class, short.class, 280 | int.class, long.class, float.class, double.class}; 281 | for (Class primitiveType : primitiveTypes) { 282 | bridgeMethodMap.put(primitiveType, primitiveType.getName() + "Bridge"); 283 | } 284 | bridgeMethodMap.put(void.class, "voidBridge"); 285 | bridgeMethodMap.put(Object.class, "referenceBridge"); 286 | } 287 | 288 | public static Method getBridgeMethod(Class returnType) { 289 | try { 290 | final String bridgeMethod = bridgeMethodMap.get(returnType.isPrimitive() ? returnType : Object.class); 291 | Logger.d(TAG, "bridge method:" + bridgeMethod + ", map:" + bridgeMethodMap); 292 | Method method = Entry64.class.getDeclaredMethod(bridgeMethod, long.class, long.class, 293 | long.class, long.class, long.class, long.class, long.class); 294 | method.setAccessible(true); 295 | return method; 296 | } catch (Throwable e) { 297 | throw new RuntimeException("error", e); 298 | } 299 | } 300 | } -------------------------------------------------------------------------------- /library/src/main/java/me/weishu/epic/art/method/Offset.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.method; 18 | 19 | import android.os.Build; 20 | 21 | import com.taobao.android.dexposed.utility.Runtime; 22 | 23 | import java.nio.ByteBuffer; 24 | import java.nio.ByteOrder; 25 | 26 | import me.weishu.epic.art.EpicNative; 27 | 28 | /** 29 | * The Offset of field in an ArtMethod 30 | */ 31 | class Offset { 32 | 33 | /** 34 | * the offset of the entry point 35 | */ 36 | static Offset ART_QUICK_CODE_OFFSET; 37 | 38 | /** 39 | * the offset of the access flag 40 | */ 41 | static Offset ART_ACCESS_FLAG_OFFSET; 42 | 43 | /** 44 | * the offset of a jni entry point 45 | */ 46 | static Offset ART_JNI_ENTRY_OFFSET; 47 | 48 | static { 49 | initFields(); 50 | } 51 | 52 | private enum BitWidth { 53 | DWORD(4), 54 | QWORD(8); 55 | 56 | BitWidth(int width) { 57 | this.width = width; 58 | } 59 | 60 | int width; 61 | } 62 | 63 | private long offset; 64 | private BitWidth length; 65 | 66 | public long getOffset() { 67 | return offset; 68 | } 69 | 70 | public void setOffset(long offset) { 71 | this.offset = offset; 72 | } 73 | 74 | public BitWidth getLength() { 75 | return length; 76 | } 77 | 78 | public void setLength(BitWidth length) { 79 | this.length = length; 80 | } 81 | 82 | public static long read(long base, Offset offset) { 83 | long address = base + offset.offset; 84 | byte[] bytes = EpicNative.get(address, offset.length.width); 85 | if (offset.length == BitWidth.DWORD) { 86 | return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getInt() & 0xFFFFFFFFL; 87 | } else { 88 | return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getLong(); 89 | } 90 | } 91 | 92 | public static void write(long base, Offset offset, long value) { 93 | long address = base + offset.offset; 94 | byte[] bytes; 95 | if (offset.length == BitWidth.DWORD) { 96 | if (value > 0xFFFFFFFFL) { 97 | throw new IllegalStateException("overflow may occur"); 98 | } else { 99 | bytes = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt((int) value).array(); 100 | } 101 | } else { 102 | bytes = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(value).array(); 103 | } 104 | EpicNative.put(bytes, address); 105 | } 106 | 107 | private static void initFields() { 108 | ART_QUICK_CODE_OFFSET = new Offset(); 109 | ART_ACCESS_FLAG_OFFSET = new Offset(); 110 | ART_JNI_ENTRY_OFFSET = new Offset(); 111 | 112 | ART_ACCESS_FLAG_OFFSET.setLength(Offset.BitWidth.DWORD); 113 | 114 | final int apiLevel = Build.VERSION.SDK_INT; 115 | if (apiLevel > 28) { 116 | throw new RuntimeException("API LEVEL: " + apiLevel + " is not supported now : ("); 117 | } 118 | 119 | if (Runtime.is64Bit()) { 120 | ART_QUICK_CODE_OFFSET.setLength(Offset.BitWidth.QWORD); 121 | ART_JNI_ENTRY_OFFSET.setLength(BitWidth.QWORD); 122 | switch (apiLevel) { 123 | case Build.VERSION_CODES.P: 124 | ART_QUICK_CODE_OFFSET.setOffset(32); 125 | ART_JNI_ENTRY_OFFSET.setOffset(24); 126 | ART_ACCESS_FLAG_OFFSET.setOffset(4); 127 | break; 128 | case Build.VERSION_CODES.O_MR1: 129 | case Build.VERSION_CODES.O: 130 | ART_QUICK_CODE_OFFSET.setOffset(40); 131 | ART_JNI_ENTRY_OFFSET.setOffset(32); 132 | ART_ACCESS_FLAG_OFFSET.setOffset(4); 133 | break; 134 | case Build.VERSION_CODES.N_MR1: 135 | case Build.VERSION_CODES.N: 136 | ART_QUICK_CODE_OFFSET.setOffset(48); 137 | ART_JNI_ENTRY_OFFSET.setOffset(40); 138 | ART_ACCESS_FLAG_OFFSET.setOffset(4); 139 | break; 140 | case Build.VERSION_CODES.M: 141 | ART_QUICK_CODE_OFFSET.setOffset(48); 142 | ART_JNI_ENTRY_OFFSET.setOffset(40); 143 | ART_ACCESS_FLAG_OFFSET.setOffset(12); 144 | break; 145 | case Build.VERSION_CODES.LOLLIPOP_MR1: 146 | ART_QUICK_CODE_OFFSET.setOffset(52); 147 | ART_JNI_ENTRY_OFFSET.setOffset(44); 148 | ART_ACCESS_FLAG_OFFSET.setOffset(20); 149 | break; 150 | case Build.VERSION_CODES.LOLLIPOP: 151 | ART_QUICK_CODE_OFFSET.setOffset(40); 152 | ART_QUICK_CODE_OFFSET.setLength(BitWidth.QWORD); 153 | ART_JNI_ENTRY_OFFSET.setOffset(32); 154 | ART_JNI_ENTRY_OFFSET.setLength(BitWidth.QWORD); 155 | ART_ACCESS_FLAG_OFFSET.setOffset(56); 156 | break; 157 | case Build.VERSION_CODES.KITKAT: 158 | ART_QUICK_CODE_OFFSET.setOffset(32); 159 | ART_ACCESS_FLAG_OFFSET.setOffset(28); 160 | break; 161 | default: 162 | throw new RuntimeException("API LEVEL: " + apiLevel + " is not supported now : ("); 163 | } 164 | } else { 165 | ART_QUICK_CODE_OFFSET.setLength(Offset.BitWidth.DWORD); 166 | ART_JNI_ENTRY_OFFSET.setLength(BitWidth.DWORD); 167 | switch (apiLevel) { 168 | case Build.VERSION_CODES.P: 169 | ART_QUICK_CODE_OFFSET.setOffset(24); 170 | ART_JNI_ENTRY_OFFSET.setOffset(20); 171 | ART_ACCESS_FLAG_OFFSET.setOffset(4); 172 | break; 173 | case Build.VERSION_CODES.O_MR1: 174 | case Build.VERSION_CODES.O: 175 | ART_QUICK_CODE_OFFSET.setOffset(28); 176 | ART_JNI_ENTRY_OFFSET.setOffset(24); 177 | ART_ACCESS_FLAG_OFFSET.setOffset(4); 178 | break; 179 | case Build.VERSION_CODES.N_MR1: 180 | case Build.VERSION_CODES.N: 181 | ART_QUICK_CODE_OFFSET.setOffset(32); 182 | ART_JNI_ENTRY_OFFSET.setOffset(28); 183 | ART_ACCESS_FLAG_OFFSET.setOffset(4); 184 | break; 185 | case Build.VERSION_CODES.M: 186 | ART_QUICK_CODE_OFFSET.setOffset(36); 187 | ART_JNI_ENTRY_OFFSET.setOffset(32); 188 | ART_ACCESS_FLAG_OFFSET.setOffset(12); 189 | break; 190 | case Build.VERSION_CODES.LOLLIPOP_MR1: 191 | ART_QUICK_CODE_OFFSET.setOffset(44); 192 | ART_JNI_ENTRY_OFFSET.setOffset(40); 193 | ART_ACCESS_FLAG_OFFSET.setOffset(20); 194 | break; 195 | case Build.VERSION_CODES.LOLLIPOP: 196 | ART_QUICK_CODE_OFFSET.setOffset(40); 197 | ART_QUICK_CODE_OFFSET.setLength(BitWidth.QWORD); 198 | ART_JNI_ENTRY_OFFSET.setOffset(32); 199 | ART_JNI_ENTRY_OFFSET.setLength(BitWidth.QWORD); 200 | ART_ACCESS_FLAG_OFFSET.setOffset(56); 201 | break; 202 | case Build.VERSION_CODES.KITKAT: 203 | ART_QUICK_CODE_OFFSET.setOffset(32); 204 | ART_ACCESS_FLAG_OFFSET.setOffset(28); 205 | break; 206 | default: 207 | throw new RuntimeException("API LEVEL: " + apiLevel + " is not supported now : ("); 208 | } 209 | } 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /library/src/main/jniLibs/armeabi/libdexposed.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guoshan-yang/ArtAndHook/463ef871eb4d5a0ccaa5670c3f825abcd9230cac/library/src/main/jniLibs/armeabi/libdexposed.so -------------------------------------------------------------------------------- /library/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | epic 3 | 4 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':jhook', ':library' 2 | --------------------------------------------------------------------------------