Skip to content

Commit

Permalink
Add initial support for N mar-v-in#11
Browse files Browse the repository at this point in the history
Also move most version dependant code into seperate classes.
  • Loading branch information
mar-v-in committed Sep 19, 2016
1 parent 1dad9ff commit 04b7ba1
Show file tree
Hide file tree
Showing 11 changed files with 616 additions and 337 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -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
8 changes: 4 additions & 4 deletions lib/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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"

Expand Down
355 changes: 28 additions & 327 deletions lib/src/main/java/de/larma/arthook/ArtMethod.java

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions lib/src/main/java/de/larma/arthook/HookPage.java
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
72 changes: 72 additions & 0 deletions lib/src/main/java/de/larma/arthook/avers/LMR0.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
101 changes: 101 additions & 0 deletions lib/src/main/java/de/larma/arthook/avers/LMR1.java
Original file line number Diff line number Diff line change
@@ -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());
}
}
164 changes: 164 additions & 0 deletions lib/src/main/java/de/larma/arthook/avers/M.java
Original file line number Diff line number Diff line change
@@ -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<Method> 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<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);
}
}
Loading

0 comments on commit 04b7ba1

Please sign in to comment.