From 04b7ba137478341a25829f8f289ba4d00bfb7bb0 Mon Sep 17 00:00:00 2001 From: Marvin W Date: Tue, 20 Sep 2016 01:28:55 +0200 Subject: [PATCH] Add initial support for N #11 Also move most version dependant code into seperate classes. --- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 4 +- lib/build.gradle | 8 +- .../main/java/de/larma/arthook/ArtMethod.java | 355 ++---------------- .../main/java/de/larma/arthook/HookPage.java | 4 +- .../java/de/larma/arthook/avers/LMR0.java | 72 ++++ .../java/de/larma/arthook/avers/LMR1.java | 101 +++++ .../main/java/de/larma/arthook/avers/M.java | 164 ++++++++ .../main/java/de/larma/arthook/avers/N.java | 165 ++++++++ .../de/larma/arthook/avers/VersionHelper.java | 75 ++++ .../de/larma/arthook/test/MyApplication.java | 3 +- 11 files changed, 616 insertions(+), 337 deletions(-) create mode 100644 lib/src/main/java/de/larma/arthook/avers/LMR0.java create mode 100644 lib/src/main/java/de/larma/arthook/avers/LMR1.java create mode 100644 lib/src/main/java/de/larma/arthook/avers/M.java create mode 100644 lib/src/main/java/de/larma/arthook/avers/N.java create mode 100644 lib/src/main/java/de/larma/arthook/avers/VersionHelper.java diff --git a/build.gradle b/build.gradle index 020c98c..3d3def4 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:1.3.0' + classpath 'com.android.tools.build:gradle:2.1.3' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 863d7de..2b85d1a 100755 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Jan 26 13:49:38 CET 2015 +#Mon Sep 19 23:10:16 CEST 2016 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.7-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip diff --git a/lib/build.gradle b/lib/build.gradle index 6620a39..aed248f 100644 --- a/lib/build.gradle +++ b/lib/build.gradle @@ -3,7 +3,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:1.3.0' + classpath 'com.android.tools.build:gradle:2.1.3' } } apply plugin: 'com.android.library' @@ -13,12 +13,12 @@ repositories { } android { - compileSdkVersion 23 - buildToolsVersion "23.0.1" + compileSdkVersion 24 + buildToolsVersion "24.0.2" defaultConfig { minSdkVersion 19 - targetSdkVersion 23 + targetSdkVersion 24 versionCode 1 versionName "1.0" diff --git a/lib/src/main/java/de/larma/arthook/ArtMethod.java b/lib/src/main/java/de/larma/arthook/ArtMethod.java index bab5e7c..179a471 100644 --- a/lib/src/main/java/de/larma/arthook/ArtMethod.java +++ b/lib/src/main/java/de/larma/arthook/ArtMethod.java @@ -16,7 +16,6 @@ package de.larma.arthook; -import java.lang.reflect.AccessibleObject; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -24,10 +23,13 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; +import de.larma.arthook.avers.VersionHelper; + import static android.os.Build.VERSION.SDK_INT; import static android.os.Build.VERSION_CODES.LOLLIPOP; import static android.os.Build.VERSION_CODES.LOLLIPOP_MR1; import static android.os.Build.VERSION_CODES.M; +import static android.os.Build.VERSION_CODES.N; import static de.larma.arthook.DebugHelper.addrHex; import static de.larma.arthook.DebugHelper.logd; @@ -43,35 +45,17 @@ public class ArtMethod { private static final String ABSTRACT_METHOD_CLASS_NAME = "java.lang.reflect.AbstractMethod"; private static final String FIELD_ART_METHOD = "artMethod"; - private static final String FIELD_ACCESS_FLAGS = "accessFlags"; - private static final int FIELD_ACCESS_FLAGS_MIRROR_INDEX = 3; - private static final String FIELD_DEX_METHOD_INDEX = "dexMethodIndex"; - private static final String FIELD_ENTRY_POINT_FROM_JNI = "entryPointFromJni"; - private static final int FIELD_ENTRY_POINT_FROM_JNI_NATIVE_INDEX = 1; - private static final String FIELD_ENTRY_POINT_FROM_INTERPRETER = "entryPointFromInterpreter"; - private static final int FIELD_ENTRY_POINT_FROM_INTERPRETER_NATIVE_INDEX = 0; - private static final String FIELD_ENTRY_POINT_FROM_QUICK_COMPILED_CODE = "entryPointFromQuickCompiledCode"; - private static final int FIELD_ENTRY_POINT_FROM_QUICK_COMPILED_CODE_NATIVE_INDEX = 2; - - private static final int LMR1_MIRROR_FIELDS = Native.is64Bit() ? 40 : 36; - private static final int LMR1_NATIVE_FIELDS_32 = 12; - private static final int LMR1_NATIVE_FIELDS_64 = 24; - private static final int LMR1_NATIVE_FIELDS = Native.is64Bit() ? LMR1_NATIVE_FIELDS_64 : LMR1_NATIVE_FIELDS_32; - private static final int LMR1_OBJECT_SIZE = LMR1_NATIVE_FIELDS + LMR1_MIRROR_FIELDS; - - private static final int M_MIRROR_FIELDS = Native.is64Bit() ? 32 : 28; - private static final int M_NATIVE_FIELDS_32 = 12; - private static final int M_NATIVE_FIELDS_64 = 24; - private static final int M_NATIVE_FIELDS = Native.is64Bit() ? M_NATIVE_FIELDS_64 : M_NATIVE_FIELDS_32; - private static final int M_OBJECT_SIZE = M_MIRROR_FIELDS + M_NATIVE_FIELDS; - - private static final boolean VERSION_LMR0 = SDK_INT == LOLLIPOP; - private static final boolean VERSION_LMR1 = SDK_INT == LOLLIPOP_MR1; - private static final boolean VERSION_L = VERSION_LMR0 || VERSION_LMR1; - private static final boolean VERSION_M = SDK_INT >= M; - - private final Object artMethod; - private Object associatedMethod; + public static final String FIELD_ACCESS_FLAGS = "accessFlags"; + public static final String FIELD_DEX_METHOD_INDEX = "dexMethodIndex"; + public static final String FIELD_ENTRY_POINT_FROM_JNI = "entryPointFromJni"; + public static final String FIELD_ENTRY_POINT_FROM_INTERPRETER = "entryPointFromInterpreter"; + public static final String FIELD_ENTRY_POINT_FROM_QUICK_COMPILED_CODE = "entryPointFromQuickCompiledCode"; + + private static final boolean VERSION_L = SDK_INT == LOLLIPOP || SDK_INT == LOLLIPOP_MR1; + private static final boolean VERSION_M_PLUS = SDK_INT >= M; + + public final Object artMethod; + public Object associatedMethod; /** * Create a new ArtMethod. @@ -80,15 +64,7 @@ public class ArtMethod { */ private ArtMethod() { try { - if (VERSION_L) { - Constructor constructor = Class.forName(ART_METHOD_CLASS_NAME).getDeclaredConstructor(); - constructor.setAccessible(true); - artMethod = constructor.newInstance(); - } else if (VERSION_M) { - artMethod = Memory.map(M_OBJECT_SIZE); - } else { - throw new RuntimeException("Platform not supported"); - } + artMethod = VersionHelper.CURRENT.createArtMethod(); } catch (Exception e) { throw new RuntimeException("Can't create new ArtMethod, is this a system running Art?", e); } @@ -141,32 +117,7 @@ static ArtMethod of(Object method) { } private Object get(String name) { - Object val = null; - if (VERSION_LMR1) { - switch (name) { - case FIELD_ENTRY_POINT_FROM_INTERPRETER: - val = getLMR1Native(FIELD_ENTRY_POINT_FROM_INTERPRETER_NATIVE_INDEX); - break; - case FIELD_ENTRY_POINT_FROM_JNI: - val = getLMR1Native(FIELD_ENTRY_POINT_FROM_JNI_NATIVE_INDEX); - break; - case FIELD_ENTRY_POINT_FROM_QUICK_COMPILED_CODE: - val = getLMR1Native(FIELD_ENTRY_POINT_FROM_QUICK_COMPILED_CODE_NATIVE_INDEX); - break; - } - } else if (VERSION_M) { - switch (name) { - case FIELD_ENTRY_POINT_FROM_INTERPRETER: - val = getMNative(FIELD_ENTRY_POINT_FROM_INTERPRETER_NATIVE_INDEX, false); - break; - case FIELD_ENTRY_POINT_FROM_JNI: - val = getMNative(FIELD_ENTRY_POINT_FROM_JNI_NATIVE_INDEX, false); - break; - case FIELD_ENTRY_POINT_FROM_QUICK_COMPILED_CODE: - val = getMNative(FIELD_ENTRY_POINT_FROM_QUICK_COMPILED_CODE_NATIVE_INDEX, false); - break; - } - } + Object val = VersionHelper.CURRENT.getArtMethodFieldNative(this, name); if (val == null) { val = get(getField(name)); } @@ -174,272 +125,22 @@ private Object get(String name) { return val; } - private long getLMR1Native(int num) { - long objectAddress = Unsafe.getObjectAddress(artMethod); - int intSize = Native.is64Bit() ? 8 : 4; - byte[] bytes = Memory.get(objectAddress + LMR1_MIRROR_FIELDS + intSize * num, intSize); - if (Native.is64Bit()) { - return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getLong(); - } else { - return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getInt() & 0xFFFFFFFFL; - } - } - - private long getMNative(int num, boolean mirror) { - long objectAddress = (long) artMethod; - int intSize = Native.is64Bit() && !mirror ? 8 : 4; - byte[] bytes = Memory.get(objectAddress + (mirror ? 0 : M_MIRROR_FIELDS) + intSize * num, intSize); - if (intSize == 8) { - return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getLong(); - } else { - return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getInt() & 0xFFFFFFFFL; - } - } - private void set(String name, Object value) { logd("Writing field: " + name + "=" + value + " from " + associatedMethod); - if (VERSION_LMR1) { - switch (name) { - case FIELD_ENTRY_POINT_FROM_INTERPRETER: - setLMR1Native(FIELD_ENTRY_POINT_FROM_INTERPRETER_NATIVE_INDEX, (Long) value); - return; - case FIELD_ENTRY_POINT_FROM_JNI: - setLMR1Native(FIELD_ENTRY_POINT_FROM_JNI_NATIVE_INDEX, (Long) value); - return; - case FIELD_ENTRY_POINT_FROM_QUICK_COMPILED_CODE: - setLMR1Native(FIELD_ENTRY_POINT_FROM_QUICK_COMPILED_CODE_NATIVE_INDEX, (Long) value); - return; - } - } else if (VERSION_M) { - switch (name) { - case FIELD_ENTRY_POINT_FROM_INTERPRETER: - setMNative(FIELD_ENTRY_POINT_FROM_INTERPRETER_NATIVE_INDEX, (Long) value); - return; - case FIELD_ENTRY_POINT_FROM_JNI: - setMNative(FIELD_ENTRY_POINT_FROM_JNI_NATIVE_INDEX, (Long) value); - return; - case FIELD_ENTRY_POINT_FROM_QUICK_COMPILED_CODE: - setMNative(FIELD_ENTRY_POINT_FROM_QUICK_COMPILED_CODE_NATIVE_INDEX, (Long) value); - return; - case FIELD_ACCESS_FLAGS: - setMMirror(FIELD_ACCESS_FLAGS_MIRROR_INDEX, (int) value); - break; - } - } - set(getField(name), value); - } - - private void setLMR1Native(int num, long value) { - long objectAddress = Unsafe.getObjectAddress(artMethod); - int intSize = Native.is64Bit() ? 8 : 4; - byte[] bytes; - if (Native.is64Bit()) { - bytes = ByteBuffer.allocate(intSize).order(ByteOrder.LITTLE_ENDIAN).putLong(value).array(); - } else { - bytes = ByteBuffer.allocate(intSize).order(ByteOrder.LITTLE_ENDIAN).putInt((int) value).array(); - } - Memory.put(bytes, objectAddress + LMR1_MIRROR_FIELDS + intSize * num); - } - - private void setMNative(int num, long value) { - long objectAddress = (long) artMethod; - int intSize = Native.is64Bit() ? 8 : 4; - byte[] bytes; - if (Native.is64Bit()) { - bytes = ByteBuffer.allocate(intSize).order(ByteOrder.LITTLE_ENDIAN).putLong(value).array(); - } else { - bytes = ByteBuffer.allocate(intSize).order(ByteOrder.LITTLE_ENDIAN).putInt((int) value).array(); + if (!VersionHelper.CURRENT.setArtMethodFieldNative(this, name, value)) { + set(getField(name), value); } - Memory.put(bytes, objectAddress + M_MIRROR_FIELDS + intSize * num); - } - - private void setMMirror(int num, int value) { - byte[] bytes = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(value).array(); - Memory.put(bytes, ((long) artMethod) + 4 * num); } @SuppressWarnings({"CloneDoesntCallSuperClone", "CloneDoesntDeclareCloneNotSupportedException"}) public ArtMethod clone() { - try { - ArtMethod clone = new ArtMethod(); - if (VERSION_L) { - /*if (VERSION_LMR1) { - long objectAddress = Unsafe.getObjectAddress(artMethod); - long map = Memory.map(LMR1_OBJECT_SIZE); - Memory.copy(objectAddress, map, LMR1_OBJECT_SIZE); - try { - long pointerOffset = Unsafe.objectFieldOffset(ArtMethod.class.getDeclaredField("artMethod")); - Unsafe.putLong(clone, pointerOffset, map); - } catch (NoSuchFieldException e) { - DebugHelper.logw(e); - } - }*/ - - // Copy fields of java.lang.reflect.ArtMethod (this.artMethod => clone.artMethod) - for (Field field : Class.forName(ART_METHOD_CLASS_NAME).getDeclaredFields()) { - field.setAccessible(true); - clone.set(field, get(field)); - } - if (VERSION_LMR1) { - clone.setEntryPointFromInterpreter(getEntryPointFromInterpreter()); - clone.setEntryPointFromJni(getEntryPointFromJni()); - clone.setEntryPointFromQuickCompiledCode(getEntryPointFromQuickCompiledCode()); - } - clone.associatedMethod = newAssociatedMethodL(associatedMethod, clone.artMethod); - } else if (VERSION_M) { - Memory.copy((long) artMethod, (long) clone.artMethod, M_OBJECT_SIZE); - clone.associatedMethod = newAssociatedMethodM(associatedMethod, clone.artMethod); - } - return clone; - } catch (Exception e) { - throw new RuntimeException("Clone not supported", e); - } - } - - /** - * Creates a new {@link java.lang.reflect.Constructor}/{@link java.lang.reflect.Method} object with the given {@link java.lang.reflect.ArtMethod}. - *

- * Only available on Android L. - * - * @param associatedMethod The former associated method or constructor (only used to decide if we need a constructor or method). - * @param artMethod The artMethod object (has to be of type {@link java.lang.reflect.ArtMethod}). - * @return The new constructor or method object. - */ - private static Object newAssociatedMethodL(Object associatedMethod, Object artMethod) { - if (associatedMethod instanceof Method) { - return newMethodL(artMethod); - } else if (associatedMethod instanceof Constructor) { - return newConstructorL(artMethod); - } - throw new IllegalArgumentException("associatedMethod has to be instance of Method or Constructor, was " + associatedMethod + "."); - } - - /** - * Creates a new {@link java.lang.reflect.Method} object with the given {@link java.lang.reflect.ArtMethod}. - *

- * Only available on Android L. - * - * @param artMethod The artMethod object (has to be of type {@link java.lang.reflect.ArtMethod}). - * @return The new method object. - */ - private static Method newMethodL(Object artMethod) { - try { - Method m = Method.class.getConstructor(Class.forName(ART_METHOD_CLASS_NAME)).newInstance(artMethod); - m.setAccessible(true); - return m; - } catch (Throwable t) { - throw new RuntimeException("Can't create new Method.", t); - } - } - - /** - * Creates a new {@link java.lang.reflect.Constructor} object with the given {@link java.lang.reflect.ArtMethod}. - *

- * Only available on Android L. - * - * @param artMethod The artMethod object (has to be of type {@link java.lang.reflect.ArtMethod}). - * @return The new constructor object. - */ - private static Constructor newConstructorL(Object artMethod) { - try { - Constructor c = Constructor.class.getConstructor(Class.forName(ART_METHOD_CLASS_NAME)).newInstance(artMethod); - c.setAccessible(true); - return c; - } catch (Throwable t) { - throw new RuntimeException("Can't create new Constructor.", t); - } - } - - /** - * Creates a new {@link java.lang.reflect.Method}/{@link java.lang.reflect.Constructor} object with the given artMethod address. - *

- * Only available on Android M. - * - * @param associatedMethod The associated method or constructor to copy. - * @param artMethod The artMethod address (has to be of type {@link java.lang.Long}). - * @return The new method object. - */ - private static Object newAssociatedMethodM(Object associatedMethod, Object artMethodAddr) { - if (associatedMethod instanceof Method) { - return newMethodM(associatedMethod, artMethodAddr); - } else if (associatedMethod instanceof Constructor) { - return newConstructorM(associatedMethod, artMethodAddr); - } - throw new IllegalArgumentException("associatedMethod has to be instance of Method or Constructor, was " + associatedMethod + "."); - } - - /** - * Creates a new {@link java.lang.reflect.Method} object with the given artMethod address. - *

- * Only available on Android M. - * - * @param associatedMethod The associated method to copy. - * @param artMethod The artMethod address (has to be of type {@link java.lang.Long}). - * @return The new method object. - */ - private static Method newMethodM(Object associatedMethod, Object artMethodAddr) { - try { - // TODO - Constructor methodConstructor = Method.class.getDeclaredConstructor(); - // we can't use methodConstructor.setAccessible(true); because Google does not like it - AccessibleObject.setAccessible(new AccessibleObject[]{methodConstructor}, true); - - Method m = methodConstructor.newInstance(); - m.setAccessible(true); - for (Field field : Class.forName(ABSTRACT_METHOD_CLASS_NAME).getDeclaredFields()) { - field.setAccessible(true); - field.set(m, field.get(associatedMethod)); - } - - Field artMethodField = Class.forName(ABSTRACT_METHOD_CLASS_NAME).getDeclaredField(FIELD_ART_METHOD); - artMethodField.setAccessible(true); - artMethodField.set(m, artMethodAddr); - - return m; - } catch (Throwable t) { - throw new RuntimeException("Can't create new Method", t); - } - } - - /** - * Creates a new {@link java.lang.reflect.Constructor} object with the given artMethod address. - *

- * Only available on Android M. - * - * @param associatedMethod The associated constructor to copy. - * @param artMethod The artMethod address (has to be of type {@link java.lang.Long}). - * @return The new constructor object. - */ - private static Constructor newConstructorM(Object associatedMethod, Object artMethodAddr) { - try { - // TODO - Constructor constructorConstructor = Constructor.class.getDeclaredConstructor(); - // we can't use constructorConstructor.setAccessible(true); because Google does not like it - AccessibleObject.setAccessible(new AccessibleObject[]{constructorConstructor}, true); - - Constructor c = constructorConstructor.newInstance(); - c.setAccessible(true); - for (Field field : Class.forName(ABSTRACT_METHOD_CLASS_NAME).getDeclaredFields()) { - field.setAccessible(true); - field.set(c, field.get(associatedMethod)); - } - - Field artMethodField = Class.forName(ABSTRACT_METHOD_CLASS_NAME).getDeclaredField(FIELD_ART_METHOD); - artMethodField.setAccessible(true); - artMethodField.set(c, artMethodAddr); - - return c; - } catch (Throwable t) { - throw new RuntimeException("Can't create new Method", t); - } + ArtMethod clone = new ArtMethod(); + VersionHelper.CURRENT.copy(this, clone); + return clone; } public void convertToMethod() { - if (VERSION_L) { - associatedMethod = newMethodL(artMethod); - } else if (VERSION_M) { - associatedMethod = newMethodM(associatedMethod, artMethod); - } + associatedMethod = VersionHelper.CURRENT.newMethod(associatedMethod, this); } public Object getAssociatedMethod() { @@ -498,21 +199,21 @@ public boolean isFinal() { public long getAddress() { if (VERSION_L) { return Unsafe.getObjectAddress(artMethod); - } else if (VERSION_M) { + } else if (VERSION_M_PLUS) { return (long) artMethod; } else { throw new RuntimeException("Not supported on your platform"); } } - private void set(Field field, Object value) { + public void set(Field field, Object value) { if (VERSION_L) { try { field.set(artMethod, value); } catch (IllegalAccessException e) { throw new RuntimeException("Not supported on your platform", e); } - } else if (VERSION_M) { + } else if (VERSION_M_PLUS) { try { field.set(associatedMethod, value); } catch (IllegalAccessException e) { @@ -523,14 +224,14 @@ private void set(Field field, Object value) { } } - private Object get(Field field) { + public Object get(Field field) { if (VERSION_L) { try { return field.get(artMethod); } catch (IllegalAccessException e) { throw new RuntimeException("Not supported on your platform", e); } - } else if (VERSION_M) { + } else if (VERSION_M_PLUS) { try { return field.get(associatedMethod); } catch (IllegalAccessException e) { @@ -550,7 +251,7 @@ private Field getField(String name) { } catch (Throwable e) { throw new RuntimeException("Field " + name + " is not available on this system", e); } - } else if (VERSION_M) { + } else if (VERSION_M_PLUS) { try { Field field = Class.forName(ABSTRACT_METHOD_CLASS_NAME).getDeclaredField(name); field.setAccessible(true); diff --git a/lib/src/main/java/de/larma/arthook/HookPage.java b/lib/src/main/java/de/larma/arthook/HookPage.java index e05e032..796c9ef 100644 --- a/lib/src/main/java/de/larma/arthook/HookPage.java +++ b/lib/src/main/java/de/larma/arthook/HookPage.java @@ -50,8 +50,8 @@ public HookPage(InstructionHelper instructionHelper, long originalAddress, int q this.originalAddress = originalAddress; this.quickCompiledCodeSize = quickCompiledCodeSize; - originalPrologue = Memory.get(originalAddress, Math.min(quickCompiledCodeSize, - instructionHelper.sizeOfDirectJump())); + originalPrologue = Memory.get(originalAddress, quickCompiledCodeSize > 0 ? Math.min(quickCompiledCodeSize, + instructionHelper.sizeOfDirectJump()) : instructionHelper.sizeOfDirectJump()); } public int getHooksCount() { diff --git a/lib/src/main/java/de/larma/arthook/avers/LMR0.java b/lib/src/main/java/de/larma/arthook/avers/LMR0.java new file mode 100644 index 0000000..50822a4 --- /dev/null +++ b/lib/src/main/java/de/larma/arthook/avers/LMR0.java @@ -0,0 +1,72 @@ +/* + * Copyright 2014-2015 Marvin Wißfeld + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.larma.arthook.avers; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +import de.larma.arthook.ArtMethod; + +public class LMR0 extends VersionHelper { + @Override + public Object createArtMethod() { + try { + Constructor constructor = Class.forName(ART_METHOD_CLASS_NAME).getDeclaredConstructor(); + constructor.setAccessible(true); + return constructor.newInstance(); + } catch (Exception e) { + return null; + } + } + + @Override + public Method newMethod(Object method, ArtMethod artMethod) { + try { + Method m = Method.class.getConstructor(Class.forName(ART_METHOD_CLASS_NAME)).newInstance(artMethod.artMethod); + m.setAccessible(true); + return m; + } catch (Throwable t) { + throw new RuntimeException("Can't create new Method.", t); + } + } + + @Override + public Constructor newConstructor(Object constructor, ArtMethod artMethod) { + try { + Constructor c = Constructor.class.getConstructor(Class.forName(ART_METHOD_CLASS_NAME)).newInstance(artMethod.artMethod); + c.setAccessible(true); + return c; + } catch (Throwable t) { + throw new RuntimeException("Can't create new Constructor.", t); + } + } + + @Override + public void copy(ArtMethod src, ArtMethod dst) { + try { + // Copy fields of java.lang.reflect.ArtMethod (this.artMethod => clone.artMethod) + for (Field field : Class.forName(ART_METHOD_CLASS_NAME).getDeclaredFields()) { + field.setAccessible(true); + dst.set(field, src.get(field)); + } + dst.associatedMethod = newAssociatedMethod(src.associatedMethod, dst); + } catch (Exception e) { + throw new RuntimeException("Clone not supported", e); + } + } +} diff --git a/lib/src/main/java/de/larma/arthook/avers/LMR1.java b/lib/src/main/java/de/larma/arthook/avers/LMR1.java new file mode 100644 index 0000000..a451d74 --- /dev/null +++ b/lib/src/main/java/de/larma/arthook/avers/LMR1.java @@ -0,0 +1,101 @@ +/* + * Copyright 2014-2015 Marvin Wißfeld + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.larma.arthook.avers; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import de.larma.arthook.ArtMethod; +import de.larma.arthook.Memory; +import de.larma.arthook.Native; +import de.larma.arthook.Unsafe; + +import static de.larma.arthook.ArtMethod.FIELD_ENTRY_POINT_FROM_INTERPRETER; +import static de.larma.arthook.ArtMethod.FIELD_ENTRY_POINT_FROM_JNI; +import static de.larma.arthook.ArtMethod.FIELD_ENTRY_POINT_FROM_QUICK_COMPILED_CODE; + +public class LMR1 extends LMR0 { + private static final int FIELD_ENTRY_POINT_FROM_INTERPRETER_NATIVE_INDEX = 0; + private static final int FIELD_ENTRY_POINT_FROM_JNI_NATIVE_INDEX = 1; + private static final int FIELD_ENTRY_POINT_FROM_QUICK_COMPILED_CODE_NATIVE_INDEX = 2; + + private static final int LMR1_MIRROR_FIELDS = Native.is64Bit() ? 40 : 36; + private static final int LMR1_NATIVE_FIELDS_32 = 12; + private static final int LMR1_NATIVE_FIELDS_64 = 24; + private static final int LMR1_NATIVE_FIELDS = Native.is64Bit() ? LMR1_NATIVE_FIELDS_64 : LMR1_NATIVE_FIELDS_32; + private static final int LMR1_OBJECT_SIZE = LMR1_NATIVE_FIELDS + LMR1_MIRROR_FIELDS; + + @Override + public Object getArtMethodFieldNative(ArtMethod artMethod, String name) { + switch (name) { + case FIELD_ENTRY_POINT_FROM_INTERPRETER: + return getNative(artMethod, FIELD_ENTRY_POINT_FROM_INTERPRETER_NATIVE_INDEX); + case FIELD_ENTRY_POINT_FROM_JNI: + return getNative(artMethod, FIELD_ENTRY_POINT_FROM_JNI_NATIVE_INDEX); + case FIELD_ENTRY_POINT_FROM_QUICK_COMPILED_CODE: + return getNative(artMethod, FIELD_ENTRY_POINT_FROM_QUICK_COMPILED_CODE_NATIVE_INDEX); + } + return super.getArtMethodFieldNative(artMethod, name); + } + + private long getNative(ArtMethod artMethod, int num) { + long objectAddress = Unsafe.getObjectAddress(artMethod.artMethod); + int intSize = Native.is64Bit() ? 8 : 4; + byte[] bytes = Memory.get(objectAddress + LMR1_MIRROR_FIELDS + intSize * num, intSize); + if (Native.is64Bit()) { + return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getLong(); + } else { + return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getInt() & 0xFFFFFFFFL; + } + } + + @Override + public boolean setArtMethodFieldNative(ArtMethod artMethod, String name, Object value) { + switch (name) { + case FIELD_ENTRY_POINT_FROM_INTERPRETER: + setNative(artMethod, FIELD_ENTRY_POINT_FROM_INTERPRETER_NATIVE_INDEX, (Long) value); + return true; + case FIELD_ENTRY_POINT_FROM_JNI: + setNative(artMethod, FIELD_ENTRY_POINT_FROM_JNI_NATIVE_INDEX, (Long) value); + return true; + case FIELD_ENTRY_POINT_FROM_QUICK_COMPILED_CODE: + setNative(artMethod, FIELD_ENTRY_POINT_FROM_QUICK_COMPILED_CODE_NATIVE_INDEX, (Long) value); + return true; + } + return super.setArtMethodFieldNative(artMethod, name, value); + } + + private void setNative(ArtMethod artMethod, int num, long value) { + long objectAddress = Unsafe.getObjectAddress(artMethod.artMethod); + int intSize = Native.is64Bit() ? 8 : 4; + byte[] bytes; + if (Native.is64Bit()) { + bytes = ByteBuffer.allocate(intSize).order(ByteOrder.LITTLE_ENDIAN).putLong(value).array(); + } else { + bytes = ByteBuffer.allocate(intSize).order(ByteOrder.LITTLE_ENDIAN).putInt((int) value).array(); + } + Memory.put(bytes, objectAddress + LMR1_MIRROR_FIELDS + intSize * num); + } + + @Override + public void copy(ArtMethod src, ArtMethod dst) { + super.copy(src, dst); + dst.setEntryPointFromInterpreter(src.getEntryPointFromInterpreter()); + dst.setEntryPointFromJni(src.getEntryPointFromJni()); + dst.setEntryPointFromQuickCompiledCode(src.getEntryPointFromQuickCompiledCode()); + } +} diff --git a/lib/src/main/java/de/larma/arthook/avers/M.java b/lib/src/main/java/de/larma/arthook/avers/M.java new file mode 100644 index 0000000..b3d0553 --- /dev/null +++ b/lib/src/main/java/de/larma/arthook/avers/M.java @@ -0,0 +1,164 @@ +/* + * Copyright 2014-2015 Marvin Wißfeld + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.larma.arthook.avers; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import de.larma.arthook.ArtMethod; +import de.larma.arthook.Memory; +import de.larma.arthook.Native; + +import static de.larma.arthook.ArtMethod.FIELD_ACCESS_FLAGS; +import static de.larma.arthook.ArtMethod.FIELD_ENTRY_POINT_FROM_INTERPRETER; +import static de.larma.arthook.ArtMethod.FIELD_ENTRY_POINT_FROM_JNI; +import static de.larma.arthook.ArtMethod.FIELD_ENTRY_POINT_FROM_QUICK_COMPILED_CODE; + +public class M extends VersionHelper { + private static final int FIELD_ENTRY_POINT_FROM_INTERPRETER_NATIVE_INDEX = 0; + private static final int FIELD_ENTRY_POINT_FROM_JNI_NATIVE_INDEX = 1; + private static final int FIELD_ENTRY_POINT_FROM_QUICK_COMPILED_CODE_NATIVE_INDEX = 2; + private static final int FIELD_ACCESS_FLAGS_MIRROR_INDEX = 3; + + private static final int M_MIRROR_FIELDS = Native.is64Bit() ? 32 : 28; + private static final int M_NATIVE_FIELDS_32 = 12; + private static final int M_NATIVE_FIELDS_64 = 24; + private static final int M_NATIVE_FIELDS = Native.is64Bit() ? M_NATIVE_FIELDS_64 : M_NATIVE_FIELDS_32; + private static final int M_OBJECT_SIZE = M_MIRROR_FIELDS + M_NATIVE_FIELDS; + + @Override + public Object createArtMethod() { + return Memory.map(M_OBJECT_SIZE); + } + + @Override + public Object getArtMethodFieldNative(ArtMethod artMethod, String name) { + switch (name) { + case FIELD_ENTRY_POINT_FROM_INTERPRETER: + return getNative((Long) artMethod.artMethod, FIELD_ENTRY_POINT_FROM_INTERPRETER_NATIVE_INDEX, false); + case FIELD_ENTRY_POINT_FROM_JNI: + return getNative((Long) artMethod.artMethod, FIELD_ENTRY_POINT_FROM_JNI_NATIVE_INDEX, false); + case FIELD_ENTRY_POINT_FROM_QUICK_COMPILED_CODE: + return getNative((Long) artMethod.artMethod, FIELD_ENTRY_POINT_FROM_QUICK_COMPILED_CODE_NATIVE_INDEX, false); + } + return super.getArtMethodFieldNative(artMethod, name); + } + + private long getNative(long objectAddress, int num, boolean mirror) { + int intSize = Native.is64Bit() && !mirror ? 8 : 4; + byte[] bytes = Memory.get(objectAddress + (mirror ? 0 : M_MIRROR_FIELDS) + intSize * num, intSize); + if (intSize == 8) { + return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getLong(); + } else { + return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getInt() & 0xFFFFFFFFL; + } + } + + @Override + public boolean setArtMethodFieldNative(ArtMethod artMethod, String name, Object value) { + switch (name) { + case FIELD_ENTRY_POINT_FROM_INTERPRETER: + setNative(artMethod, FIELD_ENTRY_POINT_FROM_INTERPRETER_NATIVE_INDEX, (Long) value); + return true; + case FIELD_ENTRY_POINT_FROM_JNI: + setNative(artMethod, FIELD_ENTRY_POINT_FROM_JNI_NATIVE_INDEX, (Long) value); + return true; + case FIELD_ENTRY_POINT_FROM_QUICK_COMPILED_CODE: + setNative(artMethod, FIELD_ENTRY_POINT_FROM_QUICK_COMPILED_CODE_NATIVE_INDEX, (Long) value); + return true; + case FIELD_ACCESS_FLAGS: + setMirror(artMethod, FIELD_ACCESS_FLAGS_MIRROR_INDEX, (int) value); + return true; + } + return super.setArtMethodFieldNative(artMethod, name, value); + } + + private void setNative(ArtMethod artMethod, int num, long value) { + long objectAddress = (long) artMethod.artMethod; + int intSize = Native.is64Bit() ? 8 : 4; + byte[] bytes; + if (Native.is64Bit()) { + bytes = ByteBuffer.allocate(intSize).order(ByteOrder.LITTLE_ENDIAN).putLong(value).array(); + } else { + bytes = ByteBuffer.allocate(intSize).order(ByteOrder.LITTLE_ENDIAN).putInt((int) value).array(); + } + Memory.put(bytes, objectAddress + M_MIRROR_FIELDS + intSize * num); + } + + private void setMirror(ArtMethod artMethod, int num, int value) { + byte[] bytes = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(value).array(); + Memory.put(bytes, ((long) artMethod.artMethod) + 4 * num); + } + + @Override + public Method newMethod(Object associatedMethod, ArtMethod artMethod) { + try { + Constructor methodConstructor = Method.class.getDeclaredConstructor(); + // we can't use methodConstructor.setAccessible(true); because Google does not like it + AccessibleObject.setAccessible(new AccessibleObject[]{methodConstructor}, true); + + Method m = methodConstructor.newInstance(); + m.setAccessible(true); + for (Field field : Class.forName(ABSTRACT_METHOD_CLASS_NAME).getDeclaredFields()) { + field.setAccessible(true); + field.set(m, field.get(associatedMethod)); + } + + Field artMethodField = Class.forName(ABSTRACT_METHOD_CLASS_NAME).getDeclaredField(FIELD_ART_METHOD); + artMethodField.setAccessible(true); + artMethodField.set(m, artMethod.artMethod); + + return m; + } catch (Throwable t) { + throw new RuntimeException("Can't create new Method", t); + } + } + + @Override + public Constructor newConstructor(Object associatedMethod, ArtMethod newArtMethod) { + try { + Constructor constructorConstructor = Constructor.class.getDeclaredConstructor(); + // we can't use constructorConstructor.setAccessible(true); because Google does not like it + AccessibleObject.setAccessible(new AccessibleObject[]{constructorConstructor}, true); + + Constructor c = constructorConstructor.newInstance(); + c.setAccessible(true); + for (Field field : Class.forName(ABSTRACT_METHOD_CLASS_NAME).getDeclaredFields()) { + field.setAccessible(true); + field.set(c, field.get(associatedMethod)); + } + + Field artMethodField = Class.forName(ABSTRACT_METHOD_CLASS_NAME).getDeclaredField(FIELD_ART_METHOD); + artMethodField.setAccessible(true); + artMethodField.set(c, newArtMethod.artMethod); + + return c; + } catch (Throwable t) { + throw new RuntimeException("Can't create new Method", t); + } + } + + @Override + public void copy(ArtMethod src, ArtMethod dst) { + Memory.copy((long) src.artMethod, (long) dst.artMethod, M_OBJECT_SIZE); + dst.associatedMethod = newAssociatedMethod(src.associatedMethod, dst); + } +} diff --git a/lib/src/main/java/de/larma/arthook/avers/N.java b/lib/src/main/java/de/larma/arthook/avers/N.java new file mode 100644 index 0000000..a0487df --- /dev/null +++ b/lib/src/main/java/de/larma/arthook/avers/N.java @@ -0,0 +1,165 @@ +/* + * Copyright 2014-2015 Marvin Wißfeld + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.larma.arthook.avers; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import de.larma.arthook.ArtMethod; +import de.larma.arthook.Memory; +import de.larma.arthook.Native; + +import static de.larma.arthook.ArtMethod.FIELD_ACCESS_FLAGS; +import static de.larma.arthook.ArtMethod.FIELD_ENTRY_POINT_FROM_JNI; +import static de.larma.arthook.ArtMethod.FIELD_ENTRY_POINT_FROM_QUICK_COMPILED_CODE; + +public class N extends VersionHelper{ + private static final int FIELD_ENTRY_POINT_FROM_JNI_NATIVE_INDEX = 2; + private static final int FIELD_ENTRY_POINT_FROM_QUICK_COMPILED_CODE_NATIVE_INDEX = 3; + private static final int FIELD_ACCESS_FLAGS_MIRROR_INDEX = 1; + + private static final int N_MIRROR_FIELDS = Native.is64Bit() ? 24 : 20; + private static final int N_NATIVE_FIELDS_32 = 16; + private static final int N_NATIVE_FIELDS_64 = 32; + private static final int N_NATIVE_FIELDS = Native.is64Bit() ? N_NATIVE_FIELDS_64 : N_NATIVE_FIELDS_32; + private static final int N_OBJECT_SIZE = N_MIRROR_FIELDS + N_NATIVE_FIELDS; + + @Override + public Object createArtMethod() { + return Memory.map(N_OBJECT_SIZE); + } + + @Override + public Object getArtMethodFieldNative(ArtMethod artMethod, String name) { + switch (name) { + case FIELD_ENTRY_POINT_FROM_JNI: + return getNative(artMethod, FIELD_ENTRY_POINT_FROM_JNI_NATIVE_INDEX, false); + case FIELD_ENTRY_POINT_FROM_QUICK_COMPILED_CODE: + return getNative(artMethod, FIELD_ENTRY_POINT_FROM_QUICK_COMPILED_CODE_NATIVE_INDEX, false); + } + return super.getArtMethodFieldNative(artMethod, name); + } + + private long getNative(ArtMethod artMethod, int num, boolean mirror) { + long objectAddress = (long) artMethod.artMethod; + int intSize = Native.is64Bit() && !mirror ? 8 : 4; + byte[] bytes = Memory.get(objectAddress + (mirror ? 0 : N_MIRROR_FIELDS) + intSize * num, intSize); + if (intSize == 8) { + return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getLong(); + } else { + return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getInt() & 0xFFFFFFFFL; + } + } + + @Override + public boolean setArtMethodFieldNative(ArtMethod artMethod, String name, Object value) { + switch (name) { + case FIELD_ENTRY_POINT_FROM_JNI: + setNative(artMethod, FIELD_ENTRY_POINT_FROM_JNI_NATIVE_INDEX, (Long) value); + return true; + case FIELD_ENTRY_POINT_FROM_QUICK_COMPILED_CODE: + setNative(artMethod, FIELD_ENTRY_POINT_FROM_QUICK_COMPILED_CODE_NATIVE_INDEX, (Long) value); + return true; + case FIELD_ACCESS_FLAGS: + setMirror(artMethod, FIELD_ACCESS_FLAGS_MIRROR_INDEX, (int) value); + return true; + } + return super.setArtMethodFieldNative(artMethod, name, value); + } + + private void setNative(ArtMethod artMethod, int num, long value) { + long objectAddress = (long) artMethod.artMethod; + int intSize = Native.is64Bit() ? 8 : 4; + byte[] bytes; + if (Native.is64Bit()) { + bytes = ByteBuffer.allocate(intSize).order(ByteOrder.LITTLE_ENDIAN).putLong(value).array(); + } else { + bytes = ByteBuffer.allocate(intSize).order(ByteOrder.LITTLE_ENDIAN).putInt((int) value).array(); + } + Memory.put(bytes, objectAddress + N_MIRROR_FIELDS + intSize * num); + } + + private void setMirror(ArtMethod artMethod, int num, int value) { + long objectAddress = (long) artMethod.artMethod; + byte[] bytes = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(value).array(); + Memory.put(bytes, objectAddress + 4 * num); + } + + @Override + public Method newMethod(Object associatedMethod, ArtMethod newArtMethod) { + try { + Constructor methodConstructor = Method.class.getDeclaredConstructor(); + // we can't use methodConstructor.setAccessible(true); because Google does not like it + // but we have some internal field for it \o/ + Field override = AccessibleObject.class.getDeclaredField("override"); + override.setAccessible(true); + override.set(methodConstructor, true); + + Method m = methodConstructor.newInstance(); + m.setAccessible(true); + for (Field field : Class.forName(ABSTRACT_METHOD_CLASS_NAME).getDeclaredFields()) { + field.setAccessible(true); + field.set(m, field.get(associatedMethod)); + } + + Field artMethodField = Class.forName(ABSTRACT_METHOD_CLASS_NAME).getDeclaredField(FIELD_ART_METHOD); + artMethodField.setAccessible(true); + artMethodField.set(m, newArtMethod.artMethod); + + return m; + } catch (Throwable t) { + throw new RuntimeException("Can't create new Method", t); + } + } + + @Override + public Constructor newConstructor(Object associatedMethod, ArtMethod newArtMethod) { + try { + Constructor constructorConstructor = Constructor.class.getDeclaredConstructor(); + // we can't use constructorConstructor.setAccessible(true); because Google does not like it + // but we have some internal field for it \o/ + Field override = AccessibleObject.class.getDeclaredField("override"); + override.setAccessible(true); + override.set(constructorConstructor, true); + + Constructor c = constructorConstructor.newInstance(); + c.setAccessible(true); + for (Field field : Class.forName(ABSTRACT_METHOD_CLASS_NAME).getDeclaredFields()) { + field.setAccessible(true); + field.set(c, field.get(associatedMethod)); + } + + Field artMethodField = Class.forName(ABSTRACT_METHOD_CLASS_NAME).getDeclaredField(FIELD_ART_METHOD); + artMethodField.setAccessible(true); + artMethodField.set(c, newArtMethod.artMethod); + + return c; + } catch (Throwable t) { + throw new RuntimeException("Can't create new Method", t); + } + } + + @Override + public void copy(ArtMethod src, ArtMethod dst) { + Memory.copy((long) src.artMethod, (long) dst.artMethod, N_OBJECT_SIZE); + dst.associatedMethod = newAssociatedMethod(src.associatedMethod, dst); + } +} diff --git a/lib/src/main/java/de/larma/arthook/avers/VersionHelper.java b/lib/src/main/java/de/larma/arthook/avers/VersionHelper.java new file mode 100644 index 0000000..f2ebf9f --- /dev/null +++ b/lib/src/main/java/de/larma/arthook/avers/VersionHelper.java @@ -0,0 +1,75 @@ +/* + * Copyright 2014-2015 Marvin Wißfeld + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.larma.arthook.avers; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +import de.larma.arthook.ArtMethod; + +import static android.os.Build.VERSION.SDK_INT; +import static android.os.Build.VERSION_CODES.LOLLIPOP; +import static android.os.Build.VERSION_CODES.LOLLIPOP_MR1; +import static android.os.Build.VERSION_CODES.M; +import static android.os.Build.VERSION_CODES.N; + +public abstract class VersionHelper { + protected static final String ART_METHOD_CLASS_NAME = "java.lang.reflect.ArtMethod"; + protected static final String ABSTRACT_METHOD_CLASS_NAME = "java.lang.reflect.AbstractMethod"; + protected static final String FIELD_ART_METHOD = "artMethod"; + + private static final boolean VERSION_LMR0 = SDK_INT == LOLLIPOP; + private static final boolean VERSION_LMR1 = SDK_INT == LOLLIPOP_MR1; + private static final boolean VERSION_L = VERSION_LMR0 || VERSION_LMR1; + private static final boolean VERSION_M = SDK_INT == M; + private static final boolean VERSION_M_PLUS = SDK_INT >= M; + private static final boolean VERSION_N_PLUS = SDK_INT >= N; + + private static final boolean FALSE = false; + + public static VersionHelper CURRENT = FALSE ? null + : VERSION_LMR0 ? new LMR0() + : VERSION_LMR1 ? new LMR1() + : VERSION_M ? new M() + : VERSION_N_PLUS ? new N() + : null; + + public abstract Object createArtMethod(); + + public Object getArtMethodFieldNative(ArtMethod artMethod, String name) { + return null; + } + + public boolean setArtMethodFieldNative(ArtMethod artMethod, String name, Object value) { + return false; + } + + public Object newAssociatedMethod(Object associatedMethod, ArtMethod artMethod) { + if (associatedMethod instanceof Method) { + return newMethod((Method) associatedMethod, artMethod); + } else if (associatedMethod instanceof Constructor) { + return newConstructor((Constructor) associatedMethod, artMethod); + } + throw new IllegalArgumentException("associatedMethod has to be instance of Method or Constructor, was " + associatedMethod + "."); + } + + public abstract Constructor newConstructor(Object originalConstructor, ArtMethod newArtMethod); + + public abstract Method newMethod(Object originalMethod, ArtMethod newArtMethod); + + public abstract void copy(ArtMethod src, ArtMethod dst); +} diff --git a/test/src/main/java/de/larma/arthook/test/MyApplication.java b/test/src/main/java/de/larma/arthook/test/MyApplication.java index 93676e7..8011fa4 100644 --- a/test/src/main/java/de/larma/arthook/test/MyApplication.java +++ b/test/src/main/java/de/larma/arthook/test/MyApplication.java @@ -64,9 +64,10 @@ public void pieceGame() { } @Hook("de.larma.arthook.test.MyApplication->pieceGame") - public void fix_pieceGame() { + public static void fix_pieceGame(MyApplication app) { Log.d(TAG, "fixed pieceGame()"); madePiece = true; + OriginalMethod.by(new $() {}).invoke(app); } @Hook("android.net.sip.SipAudioCall->startAudio")