package com.lody.legend.art;
import android.os.Build;
import com.lody.legend.utility.LegendNative;
import com.lody.legend.utility.Memory;
import com.lody.legend.utility.Runtime;
import com.lody.legend.utility.Struct;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
/**
* @author Lody
* @version 1.0
*/
public abstract class ArtMethod extends Struct {
protected Method method;
/**
* NOTE: This value is only use after M.
*/
private static int M_OBJECT_SIZE = -1;
static {
if (Build.VERSION.SDK_INT >= 23) {
try {
Field f_objectSize = Class.class.getDeclaredField("objectSize");
f_objectSize.setAccessible(true);
M_OBJECT_SIZE = f_objectSize.getInt(Method.class);
} catch (Throwable e) {
//Ignore
}
}
}
public ArtMethod(Method method) {
super(LegendNative.getMethodAddress(method));
this.method = method;
}
public static ArtMethod of(Method method) {
if (Build.VERSION.SDK_INT >= 23) {
return Runtime.is64Bit()
? new ArtMethodStructV23_64Bit(method)
: new ArtMethodStructV23(method);
}
else if (Build.VERSION.SDK_INT > 21) {
return Runtime.is64Bit()
? new ArtMethodStructV22_64Bit(method)
: new ArtMethodStructV22(method);
}
else {
return new ArtMethodStructV19(method);
}
}
/**
* This function used to create a substitute of this method,
* When this method be hooked, the substitute still pointer to origin method.
*
* @return Backup of this method
*/
public ArtMethod backup() {
try {
Class<?> abstractMethodClass = Class.forName("java.lang.reflect.AbstractMethod");
if (Build.VERSION.SDK_INT < 23) {
Class<?> artMethodClass = Class.forName("java.lang.reflect.ArtMethod");
//Get the original artMethod field
Field artMethodField = abstractMethodClass.getDeclaredField("artMethod");
if (!artMethodField.isAccessible()) {
artMethodField.setAccessible(true);
}
Object srcArtMethod = artMethodField.get(method);
Constructor<?> constructor = artMethodClass.getDeclaredConstructor();
constructor.setAccessible(true);
Object destArtMethod = constructor.newInstance();
//Fill the fields to the new method we created
for (Field field : artMethodClass.getDeclaredFields()) {
if (!field.isAccessible()) {
field.setAccessible(true);
}
field.set(destArtMethod, field.get(srcArtMethod));
}
Method newMethod = Method.class.getConstructor(artMethodClass).newInstance(destArtMethod);
newMethod.setAccessible(true);
ArtMethod artMethod = ArtMethod.of(newMethod);
artMethod.setEntryPointFromInterpreter(getEntryPointFromInterpreter());
artMethod.setEntryPointFromJni(getEntryPointFromJni());
artMethod.setEntryPointFromQuickCompiledCode(getEntryPointFromQuickCompiledCode());
//NOTICE: The clone method must set the access flags to private.
int accessFlags = getAccessFlags();
accessFlags &= ~Modifier.PUBLIC;
accessFlags |= Modifier.PRIVATE;
artMethod.setAccessFlags(accessFlags);
return artMethod;
}else {
Constructor<Method> constructor = Method.class.getDeclaredConstructor();
// we can't use constructor.setAccessible(true); because Google does not like it
AccessibleObject.setAccessible(new AccessibleObject[]{constructor}, true);
Method m = constructor.newInstance();
m.setAccessible(true);
for (Field field : abstractMethodClass.getDeclaredFields()) {
field.setAccessible(true);
field.set(m, field.get(method));
}
Field artMethodField = abstractMethodClass.getDeclaredField("artMethod");
artMethodField.setAccessible(true);
long originArtMethod = artMethodField.getLong(method);
long memoryAddress = Memory.alloc(M_OBJECT_SIZE);
byte[] data = Memory.read(originArtMethod, M_OBJECT_SIZE);
Memory.write(memoryAddress, data);
artMethodField.set(m, memoryAddress);
ArtMethod artMethod = ArtMethod.of(m);
//NOTICE: The clone method must set the access flags to private.
int accessFlags = getAccessFlags();
accessFlags &= ~Modifier.PUBLIC;
accessFlags |= Modifier.PRIVATE;
artMethod.setAccessFlags(accessFlags);
return artMethod;
}
} catch (Throwable e) {
throw new IllegalStateException("Cannot create backup method from :: " + method);
}
}
public Method getMethod() {
return method;
}
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
/////////////////////// Common Api /////////////////////////////
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
public abstract long getEntryPointFromInterpreter();
public abstract void setEntryPointFromInterpreter(long pointer_entry_point_from_interpreter);
public abstract long getEntryPointFromJni();
public abstract void setEntryPointFromJni(long pointer_entry_point_from_jni);
public abstract long getEntryPointFromQuickCompiledCode();
public abstract void setEntryPointFromQuickCompiledCode(long pointer_entry_point_from_quick_compiled_code);
public abstract int getAccessFlags();
public abstract void setAccessFlags(int newFlags);
public abstract long getDeclaringClass();
public abstract void setDeclaringClass(long declaringClass);
public abstract long getDexCacheResolvedMethods();
public abstract void setDexCacheResolvedMethods(long pointer_dex_cache_resolved_methods_);
public abstract long getDexCacheResolvedTypes();
public abstract void setDexCacheResolvedTypes(long pointer_dex_cache_resolved_types_);
public abstract int getDexCodeItemOffset();
public abstract void setDexCodeItemOffset(int offset);
public abstract int getDexMethodIndex();
public abstract void setDexMethodIndex(int index);
public abstract int getMethodIndex();
public abstract void setMethodIndex(int index);
}