Commit 5c250a7a authored by swift_gan's avatar swift_gan

add dexmaker cache

parent 0b884655
...@@ -74,7 +74,7 @@ public class SandHook { ...@@ -74,7 +74,7 @@ public class SandHook {
public static synchronized void hook(HookWrapper.HookEntity entity) throws HookErrorException { public static synchronized void hook(HookWrapper.HookEntity entity) throws HookErrorException {
if (entity == null) if (entity == null)
throw new HookErrorException("null entity"); throw new HookErrorException("null hook entity");
Member target = entity.target; Member target = entity.target;
Method hook = entity.hook; Method hook = entity.hook;
......
...@@ -26,7 +26,7 @@ android { ...@@ -26,7 +26,7 @@ android {
dependencies { dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs') implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.linkedin.dexmaker:dexmaker:2.21.0' implementation 'com.jakewharton.android.repackaged:dalvik-dx:9.0.0_r3'
compileOnly project(':hooklib') compileOnly project(':hooklib')
} }
......
/*
* Copyright (C) 2017 The Android Open Source Project
*
* 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 com.android.dx;
import com.android.dx.dex.file.ClassDefItem;
import com.android.dx.rop.annotation.Annotation;
import com.android.dx.rop.annotation.AnnotationVisibility;
import com.android.dx.rop.annotation.Annotations;
import com.android.dx.rop.annotation.NameValuePair;
import com.android.dx.rop.cst.*;
import java.lang.annotation.ElementType;
import java.util.HashMap;
/**
* Identifies an annotation on a program element, see {@link ElementType}.
*
* Currently it is only targeting Class, Method, Field and Parameter because those are supported by
* {@link com.android.dx.dex.file.AnnotationsDirectoryItem} so far.
*
* <p><strong>NOTE:</strong>
* So far it only supports adding method annotation. The annotation of class, field and parameter
* will be implemented later.
*
* <p><strong>WARNING:</strong>
* The declared element of an annotation type should either have a default value or be set a value via
* {@code AnnotationId.set(Element)}. Otherwise it will incur
* {@link java.lang.annotation.IncompleteAnnotationException} when accessing the annotation element
* through reflection. The example is as follows:
* <pre>
* {@code @Retention(RetentionPolicy.RUNTIME)}
* {@code @Target({ElementType.METHOD})}
* {@code @interface MethodAnnotation {
* boolean elementBoolean();
* // boolean elementBoolean() default false;
* }
*
* TypeId<?> GENERATED = TypeId.get("LGenerated;");
* MethodId<?, Void> methodId = GENERATED.getMethod(VOID, "call");
* Code code = dexMaker.declare(methodId, PUBLIC);
* code.returnVoid();
*
* TypeId<MethodAnnotation> annotationTypeId = TypeId.get(MethodAnnotation.class);
* AnnotationId<?, MethodAnnotation> annotationId = AnnotationId.get(GENERATED,
* annotationTypeId, ElementType.METHOD);
*
* AnnotationId.Element element = new AnnotationId.Element("elementBoolean", true);
* annotationId.set(element);
* annotationId.addToMethod(dexMaker, methodId);
* }
* </pre>
*
* @param <D> the type that declares the program element.
* @param <V> the annotation type. It should be a known type before compile.
*/
public final class AnnotationId<D, V> {
private final TypeId<D> declaringType;
private final TypeId<V> type;
/** The type of program element to be annotated */
private final ElementType annotatedElement;
/** The elements this annotation holds */
private final HashMap<String, NameValuePair> elements;
private AnnotationId(TypeId<D> declaringType, TypeId<V> type, ElementType annotatedElement) {
this.declaringType = declaringType;
this.type = type;
this.annotatedElement = annotatedElement;
this.elements = new HashMap<>();
}
/**
* Construct an instance. It initially contains no elements.
*
* @param declaringType the type declaring the program element.
* @param type the annotation type.
* @param annotatedElement the program element type to be annotated.
* @return an annotation {@code AnnotationId<D,V>} instance.
*/
public static <D, V> AnnotationId<D, V> get(TypeId<D> declaringType, TypeId<V> type,
ElementType annotatedElement) {
if (annotatedElement != ElementType.TYPE &&
annotatedElement != ElementType.METHOD &&
annotatedElement != ElementType.FIELD &&
annotatedElement != ElementType.PARAMETER) {
throw new IllegalArgumentException("element type is not supported to annotate yet.");
}
return new AnnotationId<>(declaringType, type, annotatedElement);
}
/**
* Set an annotation element of this instance.
* If there is a preexisting element with the same name, it will be
* replaced by this method.
*
* @param element {@code non-null;} the annotation element to be set.
*/
public void set(Element element) {
if (element == null) {
throw new NullPointerException("element == null");
}
CstString pairName = new CstString(element.getName());
Constant pairValue = Element.toConstant(element.getValue());
NameValuePair nameValuePair = new NameValuePair(pairName, pairValue);
elements.put(element.getName(), nameValuePair);
}
/**
* Add this annotation to a method.
*
* @param dexMaker DexMaker instance.
* @param method Method to be added to.
*/
public void addToMethod(DexMaker dexMaker, MethodId<?, ?> method) {
if (annotatedElement != ElementType.METHOD) {
throw new IllegalStateException("This annotation is not for method");
}
if (!method.declaringType.equals(declaringType)) {
throw new IllegalArgumentException("Method" + method + "'s declaring type is inconsistent with" + this);
}
ClassDefItem classDefItem = dexMaker.getTypeDeclaration(declaringType).toClassDefItem();
if (classDefItem == null) {
throw new NullPointerException("No class defined item is found");
} else {
CstMethodRef cstMethodRef = method.constant;
if (cstMethodRef == null) {
throw new NullPointerException("Method reference is NULL");
} else {
// Generate CstType
CstType cstType = CstType.intern(type.ropType);
// Generate Annotation
Annotation annotation = new Annotation(cstType, AnnotationVisibility.RUNTIME);
// Add generated annotation
Annotations annotations = new Annotations();
for (NameValuePair nvp : elements.values()) {
annotation.add(nvp);
}
annotations.add(annotation);
classDefItem.addMethodAnnotations(cstMethodRef, annotations, dexMaker.getDexFile());
}
}
}
/**
* A wrapper of <code>NameValuePair</code> represents a (name, value) pair used as the contents
* of an annotation.
*
* An {@code Element} instance is stored in {@code AnnotationId.elements} by calling {@code
* AnnotationId.set(Element)}.
*
* <p><strong>WARNING: </strong></p>
* the name should be exact same as the annotation element declared in the annotation type
* which is referred by field {@code AnnotationId.type},otherwise the annotation will fail
* to add and {@code java.lang.reflect.Method.getAnnotations()} will return nothing.
*
*/
public static final class Element {
/** {@code non-null;} the name */
private final String name;
/** {@code non-null;} the value */
private final Object value;
/**
* Construct an instance.
*
* @param name {@code non-null;} the name
* @param value {@code non-null;} the value
*/
public Element(String name, Object value) {
if (name == null) {
throw new NullPointerException("name == null");
}
if (value == null) {
throw new NullPointerException("value == null");
}
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public Object getValue() {
return value;
}
/** {@inheritDoc} */
@Override
public String toString() {
return "[" + name + ", " + value + "]";
}
/** {@inheritDoc} */
@Override
public int hashCode() {
return name.hashCode() * 31 + value.hashCode();
}
/** {@inheritDoc} */
@Override
public boolean equals(Object other) {
if (! (other instanceof Element)) {
return false;
}
Element otherElement = (Element) other;
return name.equals(otherElement.name)
&& value.equals(otherElement.value);
}
/**
* Convert a value of an element to a {@code Constant}.
* <p><strong>Warning:</strong> Array or TypeId value is not supported yet.
*
* @param value an annotation element value.
* @return a Constant
*/
static Constant toConstant(Object value) {
Class clazz = value.getClass();
if (clazz.isEnum()) {
CstString descriptor = new CstString(TypeId.get(clazz).getName());
CstString name = new CstString(((Enum)value).name());
CstNat cstNat = new CstNat(name, descriptor);
return new CstEnumRef(cstNat);
} else if (clazz.isArray()) {
throw new UnsupportedOperationException("Array is not supported yet");
} else if (value instanceof TypeId) {
throw new UnsupportedOperationException("TypeId is not supported yet");
} else {
return Constants.getConstant(value);
}
}
}
}
/*
* Copyright (C) 2012 The Android Open Source Project
*
* 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 com.android.dx;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
/**
* Uses heuristics to guess the application's private data directory.
*/
class AppDataDirGuesser {
// Copied from UserHandle, indicates range of uids allocated for a user.
public static final int PER_USER_RANGE = 100000;
public File guess() {
try {
ClassLoader classLoader = guessSuitableClassLoader();
// Check that we have an instance of the PathClassLoader.
Class<?> clazz = Class.forName("dalvik.system.PathClassLoader");
clazz.cast(classLoader);
// Use the toString() method to calculate the data directory.
String pathFromThisClassLoader = getPathFromThisClassLoader(classLoader, clazz);
File[] results = guessPath(pathFromThisClassLoader);
if (results.length > 0) {
return results[0];
}
} catch (ClassCastException ignored) {
} catch (ClassNotFoundException ignored) {
}
return null;
}
private ClassLoader guessSuitableClassLoader() {
return AppDataDirGuesser.class.getClassLoader();
}
private String getPathFromThisClassLoader(ClassLoader classLoader, Class<?> pathClassLoaderClass) {
// Prior to ICS, we can simply read the "path" field of the
// PathClassLoader.
try {
Field pathField = pathClassLoaderClass.getDeclaredField("path");
pathField.setAccessible(true);
return (String) pathField.get(classLoader);
} catch (NoSuchFieldException ignored) {
} catch (IllegalAccessException ignored) {
} catch (ClassCastException ignored) {
}
// Parsing toString() method: yuck. But no other way to get the path.
String result = classLoader.toString();
return processClassLoaderString(result);
}
/**
* Given the result of a ClassLoader.toString() call, process the result so that guessPath
* can use it. There are currently two variants. For Android 4.3 and later, the string
* "DexPathList" should be recognized and the array of dex path elements is parsed. for
* earlier versions, the last nested array ('[' ... ']') is enclosing the string we are
* interested in.
*/
static String processClassLoaderString(String input) {
if (input.contains("DexPathList")) {
return processClassLoaderString43OrLater(input);
} else {
return processClassLoaderString42OrEarlier(input);
}
}
private static String processClassLoaderString42OrEarlier(String input) {
/* The toString output looks like this:
* dalvik.system.PathClassLoader[dexPath=path/to/apk,libraryPath=path/to/libs]
*/
int index = input.lastIndexOf('[');
input = (index == -1) ? input : input.substring(index + 1);
index = input.indexOf(']');
input = (index == -1) ? input : input.substring(0, index);
return input;
}
private static String processClassLoaderString43OrLater(String input) {
/* The toString output looks like this:
* dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/{NAME}", ...], nativeLibraryDirectories=[...]]]
*/
int start = input.indexOf("DexPathList") + "DexPathList".length();
if (input.length() > start + 4) { // [[ + ]]
String trimmed = input.substring(start);
int end = trimmed.indexOf(']');
if (trimmed.charAt(0) == '[' && trimmed.charAt(1) == '[' && end >= 0) {
trimmed = trimmed.substring(2, end);
// Comma-separated list, Arrays.toString output.
String split[] = trimmed.split(",");
// Clean up parts. Each path element is the type of the element plus the path in
// quotes.
for (int i = 0; i < split.length; i++) {
int quoteStart = split[i].indexOf('"');
int quoteEnd = split[i].lastIndexOf('"');
if (quoteStart > 0 && quoteStart < quoteEnd) {
split[i] = split[i].substring(quoteStart + 1, quoteEnd);
}
}
// Need to rejoin components.
StringBuilder sb = new StringBuilder();
for (String s : split) {
if (sb.length() > 0) {
sb.append(':');
}
sb.append(s);
}
return sb.toString();
}
}
// This is technically a parsing failure. Return the original string, maybe a later
// stage can still salvage this.
return input;
}
File[] guessPath(String input) {
List<File> results = new ArrayList<>();
for (String potential : splitPathList(input)) {
if (!potential.startsWith("/data/app/")) {
continue;
}
int start = "/data/app/".length();
int end = potential.lastIndexOf(".apk");
if (end != potential.length() - 4) {
continue;
}
int dash = potential.indexOf("-");
if (dash != -1) {
end = dash;
}
String packageName = potential.substring(start, end);
File dataDir = getWriteableDirectory("/data/data/" + packageName);
if (dataDir == null) {
// If we can't access "/data/data", try to guess user specific data directory.
dataDir = guessUserDataDirectory(packageName);
}
if (dataDir != null) {
File cacheDir = new File(dataDir, "cache");
// The cache directory might not exist -- create if necessary
if (fileOrDirExists(cacheDir) || cacheDir.mkdir()) {
if (isWriteableDirectory(cacheDir)) {
results.add(cacheDir);
}
}
}
}
return results.toArray(new File[results.size()]);
}
static String[] splitPathList(String input) {
String trimmed = input;
if (input.startsWith("dexPath=")) {
int start = "dexPath=".length();
int end = input.indexOf(',');
trimmed = (end == -1) ? input.substring(start) : input.substring(start, end);
}
return trimmed.split(":");
}
boolean fileOrDirExists(File file) {
return file.exists();
}
boolean isWriteableDirectory(File file) {
return file.isDirectory() && file.canWrite();
}
Integer getProcessUid() {
/* Uses reflection to try to fetch process UID. It will only work when executing on
* Android device. Otherwise, returns null.
*/
try {
Method myUid = Class.forName("android.os.Process").getMethod("myUid");
// Invoke the method on a null instance, since it's a static method.
return (Integer) myUid.invoke(/* instance= */ null);
} catch (Exception e) {
// Catch any exceptions thrown and default to returning a null.
return null;
}
}
File guessUserDataDirectory(String packageName) {
Integer uid = getProcessUid();
if (uid == null) {
// If we couldn't retrieve process uid, return null.
return null;
}
// We're trying to get the ID of the Android user that's running the process. It can be
// inferred from the UID of the current process.
int userId = uid / PER_USER_RANGE;
return getWriteableDirectory(String.format("/data/user/%d/%s", userId, packageName));
}
private File getWriteableDirectory(String pathName) {
File dir = new File(pathName);
return isWriteableDirectory(dir) ? dir : null;
}
}
/*
* Copyright (C) 2011 The Android Open Source Project
*
* 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 com.android.dx;
import com.android.dx.rop.code.Rop;
import com.android.dx.rop.code.Rops;
import com.android.dx.rop.type.TypeList;
/**
* An operation on two values of the same type.
*
* <p>Math operations ({@link #ADD}, {@link #SUBTRACT}, {@link #MULTIPLY},
* {@link #DIVIDE}, and {@link #REMAINDER}) support ints, longs, floats and
* doubles.
*
* <p>Bit operations ({@link #AND}, {@link #OR}, {@link #XOR}, {@link
* #SHIFT_LEFT}, {@link #SHIFT_RIGHT}, {@link #UNSIGNED_SHIFT_RIGHT}) support
* ints and longs.
*
* <p>Division by zero behaves differently depending on the operand type.
* For int and long operands, {@link #DIVIDE} and {@link #REMAINDER} throw
* {@link ArithmeticException} if {@code b == 0}. For float and double operands,
* the operations return {@code NaN}.
*/
public enum BinaryOp {
/** {@code a + b} */
ADD() {
@Override
Rop rop(TypeList types) {
return Rops.opAdd(types);
}
},
/** {@code a - b} */
SUBTRACT() {
@Override
Rop rop(TypeList types) {
return Rops.opSub(types);
}
},
/** {@code a * b} */
MULTIPLY() {
@Override
Rop rop(TypeList types) {
return Rops.opMul(types);
}
},
/** {@code a / b} */
DIVIDE() {
@Override
Rop rop(TypeList types) {
return Rops.opDiv(types);
}
},
/** {@code a % b} */
REMAINDER() {
@Override
Rop rop(TypeList types) {
return Rops.opRem(types);
}
},
/** {@code a & b} */
AND() {
@Override
Rop rop(TypeList types) {
return Rops.opAnd(types);
}
},
/** {@code a | b} */
OR() {
@Override
Rop rop(TypeList types) {
return Rops.opOr(types);
}
},
/** {@code a ^ b} */
XOR() {
@Override
Rop rop(TypeList types) {
return Rops.opXor(types);
}
},
/** {@code a << b} */
SHIFT_LEFT() {
@Override
Rop rop(TypeList types) {
return Rops.opShl(types);
}
},
/** {@code a >> b} */
SHIFT_RIGHT() {
@Override
Rop rop(TypeList types) {
return Rops.opShr(types);
}
},
/** {@code a >>> b} */
UNSIGNED_SHIFT_RIGHT() {
@Override
Rop rop(TypeList types) {
return Rops.opUshr(types);
}
};
abstract Rop rop(TypeList types);
}
This diff is collapsed.
/*
* Copyright (C) 2011 The Android Open Source Project
*
* 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 com.android.dx;
import com.android.dx.rop.code.Rop;
import com.android.dx.rop.code.Rops;
import com.android.dx.rop.type.TypeList;
/**
* A comparison between two values of the same type.
*/
public enum Comparison {
/** {@code a < b}. Supports int only. */
LT() {
@Override
Rop rop(TypeList types) {
return Rops.opIfLt(types);
}
},
/** {@code a <= b}. Supports int only. */
LE() {
@Override
Rop rop(TypeList types) {
return Rops.opIfLe(types);
}
},
/** {@code a == b}. Supports int and reference types. */
EQ() {
@Override
Rop rop(TypeList types) {
return Rops.opIfEq(types);
}
},
/** {@code a >= b}. Supports int only. */
GE() {
@Override
Rop rop(TypeList types) {
return Rops.opIfGe(types);
}
},
/** {@code a > b}. Supports int only. */
GT() {
@Override
Rop rop(TypeList types) {
return Rops.opIfGt(types);
}
},
/** {@code a != b}. Supports int and reference types. */
NE() {
@Override
Rop rop(TypeList types) {
return Rops.opIfNe(types);
}
};
abstract Rop rop(TypeList types);
}
/*
* Copyright (C) 2011 The Android Open Source Project
*
* 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 com.android.dx;
import com.android.dx.rop.cst.CstBoolean;
import com.android.dx.rop.cst.CstByte;
import com.android.dx.rop.cst.CstChar;
import com.android.dx.rop.cst.CstDouble;
import com.android.dx.rop.cst.CstFloat;
import com.android.dx.rop.cst.CstInteger;
import com.android.dx.rop.cst.CstKnownNull;
import com.android.dx.rop.cst.CstLong;
import com.android.dx.rop.cst.CstShort;
import com.android.dx.rop.cst.CstString;
import com.android.dx.rop.cst.CstType;
import com.android.dx.rop.cst.TypedConstant;
/**
* Factory for rop constants.
*/
final class Constants {
private Constants() {}
/**
* Returns a rop constant for the specified value.
*
* @param value null, a boxed primitive, String, Class, or TypeId.
*/
static TypedConstant getConstant(Object value) {
if (value == null) {
return CstKnownNull.THE_ONE;
} else if (value instanceof Boolean) {
return CstBoolean.make((Boolean) value);
} else if (value instanceof Byte) {
return CstByte.make((Byte) value);
} else if (value instanceof Character) {
return CstChar.make((Character) value);
} else if (value instanceof Double) {
return CstDouble.make(Double.doubleToLongBits((Double) value));
} else if (value instanceof Float) {
return CstFloat.make(Float.floatToIntBits((Float) value));
} else if (value instanceof Integer) {
return CstInteger.make((Integer) value);
} else if (value instanceof Long) {
return CstLong.make((Long) value);
} else if (value instanceof Short) {
return CstShort.make((Short) value);
} else if (value instanceof String) {
return new CstString((String) value);
} else if (value instanceof Class) {
return new CstType(TypeId.get((Class<?>) value).ropType);
} else if (value instanceof TypeId) {
return new CstType(((TypeId) value).ropType);
} else {
throw new UnsupportedOperationException("Not a constant: " + value);
}
}
}
This diff is collapsed.
/*
* Copyright (C) 2011 The Android Open Source Project
*
* 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 com.android.dx;
import com.android.dx.rop.cst.CstFieldRef;
import com.android.dx.rop.cst.CstNat;
import com.android.dx.rop.cst.CstString;
/**
* Identifies a field.
*
* @param <D> the type declaring this field
* @param <V> the type of value this field holds
*/
public final class FieldId<D, V> {
final TypeId<D> declaringType;
final TypeId<V> type;
final String name;
/** cached converted state */
final CstNat nat;
final CstFieldRef constant;
FieldId(TypeId<D> declaringType, TypeId<V> type, String name) {
if (declaringType == null || type == null || name == null) {
throw new NullPointerException();
}
this.declaringType = declaringType;
this.type = type;
this.name = name;
this.nat = new CstNat(new CstString(name), new CstString(type.name));
this.constant = new CstFieldRef(declaringType.constant, nat);
}
public TypeId<D> getDeclaringType() {
return declaringType;
}
public TypeId<V> getType() {
return type;
}
public String getName() {
return name;
}
@Override
public boolean equals(Object o) {
return o instanceof FieldId
&& ((FieldId<?, ?>) o).declaringType.equals(declaringType)
&& ((FieldId<?, ?>) o).name.equals(name);
}
@Override
public int hashCode() {
return declaringType.hashCode() + 37 * name.hashCode();
}
@Override
public String toString() {
return declaringType + "." + name;
}
}
/*
* Copyright (C) 2011 The Android Open Source Project
*
* 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 com.android.dx;
import com.android.dx.rop.code.BasicBlock;
import com.android.dx.rop.code.Insn;
import com.android.dx.rop.code.InsnList;
import com.android.dx.util.IntList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* A branch target in a list of instructions.
*/
public final class Label {
final List<Insn> instructions = new ArrayList<>();
Code code;
boolean marked = false;
/** an immutable list of labels corresponding to the types in the catch list */
List<Label> catchLabels = Collections.emptyList();
/** contains the next instruction if no branch occurs */
Label primarySuccessor;
/** contains the instruction to jump to if the if is true */
Label alternateSuccessor;
int id = -1;
public Label() {}
boolean isEmpty() {
return instructions.isEmpty();
}
void compact() {
for (int i = 0; i < catchLabels.size(); i++) {
while (catchLabels.get(i).isEmpty()) {
catchLabels.set(i, catchLabels.get(i).primarySuccessor);
}
}
while (primarySuccessor != null && primarySuccessor.isEmpty()) {
primarySuccessor = primarySuccessor.primarySuccessor;
}
while (alternateSuccessor != null && alternateSuccessor.isEmpty()) {
alternateSuccessor = alternateSuccessor.primarySuccessor;
}
}
BasicBlock toBasicBlock() {
InsnList result = new InsnList(instructions.size());
for (int i = 0; i < instructions.size(); i++) {
result.set(i, instructions.get(i));
}
result.setImmutable();
int primarySuccessorIndex = -1;
IntList successors = new IntList();
for (Label catchLabel : catchLabels) {
successors.add(catchLabel.id);
}
if (primarySuccessor != null) {
primarySuccessorIndex = primarySuccessor.id;
successors.add(primarySuccessorIndex);
}
if (alternateSuccessor != null) {
successors.add(alternateSuccessor.id);
}
successors.setImmutable();
return new BasicBlock(id, result, successors, primarySuccessorIndex);
}
}
/*
* Copyright (C) 2011 The Android Open Source Project
*
* 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 com.android.dx;
import com.android.dx.rop.code.RegisterSpec;
/**
* A temporary variable that holds a single value of a known type.
*/
public final class Local<T> {
private final Code code;
final TypeId<T> type;
private int reg = -1;
private RegisterSpec spec;
private Local(Code code, TypeId<T> type) {
this.code = code;
this.type = type;
}
static <T> Local<T> get(Code code, TypeId<T> type) {
return new Local<T>(code, type);
}
/**
* Assigns registers to this local.
*
* @return the number of registers required.
*/
int initialize(int nextAvailableRegister) {
this.reg = nextAvailableRegister;
this.spec = RegisterSpec.make(nextAvailableRegister, type.ropType);
return size();
}
/**
* Returns the number of registered required to hold this local.
*/
int size() {
return type.ropType.getCategory();
}
RegisterSpec spec() {
if (spec == null) {
code.initializeLocals();
if (spec == null) {
throw new AssertionError();
}
}
return spec;
}
public TypeId getType() {
return type;
}
@Override
public String toString() {
return "v" + reg + "(" + type + ")";
}
}
/*
* Copyright (C) 2011 The Android Open Source Project
*
* 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 com.android.dx;
import com.android.dx.rop.cst.CstMethodRef;
import com.android.dx.rop.cst.CstNat;
import com.android.dx.rop.cst.CstString;
import com.android.dx.rop.type.Prototype;
import java.util.List;
/**
* Identifies a method or constructor.
*
* @param <D> the type declaring this field
* @param <R> the return type of this method
*/
public final class MethodId<D, R> {
final TypeId<D> declaringType;
final TypeId<R> returnType;
final String name;
final TypeList parameters;
/** cached converted state */
final CstNat nat;
final CstMethodRef constant;
MethodId(TypeId<D> declaringType, TypeId<R> returnType, String name, TypeList parameters) {
if (declaringType == null || returnType == null || name == null || parameters == null) {
throw new NullPointerException();
}
this.declaringType = declaringType;
this.returnType = returnType;
this.name = name;
this.parameters = parameters;
this.nat = new CstNat(new CstString(name), new CstString(descriptor(false)));
this.constant = new CstMethodRef(declaringType.constant, nat);
}
public TypeId<D> getDeclaringType() {
return declaringType;
}
public TypeId<R> getReturnType() {
return returnType;
}
/**
* Returns true if this method is a constructor for its declaring class.
*/
public boolean isConstructor() {
return name.equals("<init>");
}
/**
* Returns true if this method is the static initializer for its declaring class.
*/
public boolean isStaticInitializer() {
return name.equals("<clinit>");
}
/**
* Returns the method's name. This is "&lt;init&gt;" if this is a constructor
* or "&lt;clinit&gt;" if a static initializer
*/
public String getName() {
return name;
}
public List<TypeId<?>> getParameters() {
return parameters.asList();
}
/**
* Returns a descriptor like "(Ljava/lang/Class;[I)Ljava/lang/Object;".
*/
String descriptor(boolean includeThis) {
StringBuilder result = new StringBuilder();
result.append("(");
if (includeThis) {
result.append(declaringType.name);
}
for (TypeId t : parameters.types) {
result.append(t.name);
}
result.append(")");
result.append(returnType.name);
return result.toString();
}
Prototype prototype(boolean includeThis) {
return Prototype.intern(descriptor(includeThis));
}
@Override
public boolean equals(Object o) {
return o instanceof MethodId
&& ((MethodId<?, ?>) o).declaringType.equals(declaringType)
&& ((MethodId<?, ?>) o).name.equals(name)
&& ((MethodId<?, ?>) o).parameters.equals(parameters)
&& ((MethodId<?, ?>) o).returnType.equals(returnType);
}
@Override
public int hashCode() {
int result = 17;
result = 31 * result + declaringType.hashCode();
result = 31 * result + name.hashCode();
result = 31 * result + parameters.hashCode();
result = 31 * result + returnType.hashCode();
return result;
}
@Override
public String toString() {
return declaringType + "." + name + "(" + parameters + ")";
}
}
/*
* Copyright (C) 2011 The Android Open Source Project
*
* 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 com.android.dx;
import com.android.dx.rop.cst.CstType;
import java.util.HashMap;
import java.util.Map;
/**
* A primitive type, interface or class.
*
* <p><strong>Warning:</strong> Use care when dealing with boxed primitive
* types. Java's lack of support for parameterized primitive types means that
* a primitive type like {@code int} and its boxed type {@code
* java.lang.Integer} have the same type parameter: {@code TypeId<Integer>}.
* These types are different and it will be a runtime error if the boxed type
* {@code java.lang.Integer} is used where the primitive type {@code int} is
* expected.
*/
public final class TypeId<T> {
/** The {@code boolean} primitive type. */
public static final TypeId<Boolean> BOOLEAN = new TypeId<>(com.android.dx.rop.type.Type.BOOLEAN);
/** The {@code byte} primitive type. */
public static final TypeId<Byte> BYTE = new TypeId<>(com.android.dx.rop.type.Type.BYTE);
/** The {@code char} primitive type. */
public static final TypeId<Character> CHAR = new TypeId<>(com.android.dx.rop.type.Type.CHAR);
/** The {@code double} primitive type. */
public static final TypeId<Double> DOUBLE = new TypeId<>(com.android.dx.rop.type.Type.DOUBLE);
/** The {@code float} primitive type. */
public static final TypeId<Float> FLOAT = new TypeId<>(com.android.dx.rop.type.Type.FLOAT);
/** The {@code int} primitive type. */
public static final TypeId<Integer> INT = new TypeId<>(com.android.dx.rop.type.Type.INT);
/** The {@code long} primitive type. */
public static final TypeId<Long> LONG = new TypeId<>(com.android.dx.rop.type.Type.LONG);
/** The {@code short} primitive type. */
public static final TypeId<Short> SHORT = new TypeId<>(com.android.dx.rop.type.Type.SHORT);
/** The {@code void} primitive type. Only used as a return type. */
public static final TypeId<Void> VOID = new TypeId<>(com.android.dx.rop.type.Type.VOID);
/** The {@code Object} type. */
public static final TypeId<Object> OBJECT = new TypeId<>(com.android.dx.rop.type.Type.OBJECT);
/** The {@code String} type. */
public static final TypeId<String> STRING = new TypeId<>(com.android.dx.rop.type.Type.STRING);
private static final Map<Class<?>, TypeId<?>> PRIMITIVE_TO_TYPE = new HashMap<>();
static {
PRIMITIVE_TO_TYPE.put(boolean.class, BOOLEAN);
PRIMITIVE_TO_TYPE.put(byte.class, BYTE);
PRIMITIVE_TO_TYPE.put(char.class, CHAR);
PRIMITIVE_TO_TYPE.put(double.class, DOUBLE);
PRIMITIVE_TO_TYPE.put(float.class, FLOAT);
PRIMITIVE_TO_TYPE.put(int.class, INT);
PRIMITIVE_TO_TYPE.put(long.class, LONG);
PRIMITIVE_TO_TYPE.put(short.class, SHORT);
PRIMITIVE_TO_TYPE.put(void.class, VOID);
}
final String name;
/** cached converted values */
final com.android.dx.rop.type.Type ropType;
final CstType constant;
TypeId(com.android.dx.rop.type.Type ropType) {
this(ropType.getDescriptor(), ropType);
}
TypeId(String name, com.android.dx.rop.type.Type ropType) {
if (name == null || ropType == null) {
throw new NullPointerException();
}
this.name = name;
this.ropType = ropType;
this.constant = CstType.intern(ropType);
}
/**
* @param name a descriptor like "Ljava/lang/Class;".
*/
public static <T> TypeId<T> get(String name) {
return new TypeId<>(name, com.android.dx.rop.type.Type.internReturnType(name));
}
public static <T> TypeId<T> get(Class<T> type) {
if (type.isPrimitive()) {
// guarded by equals
@SuppressWarnings("unchecked")
TypeId<T> result = (TypeId<T>) PRIMITIVE_TO_TYPE.get(type);
return result;
}
String name = type.getName().replace('.', '/');
return get(type.isArray() ? name : 'L' + name + ';');
}
public <V> FieldId<T, V> getField(TypeId<V> type, String name) {
return new FieldId<>(this, type, name);
}
public MethodId<T, Void> getConstructor(TypeId<?>... parameters) {
return new MethodId<>(this, VOID, "<init>", new TypeList(parameters));
}
public MethodId<T, Void> getStaticInitializer() {
return new MethodId<>(this, VOID, "<clinit>", new TypeList(new TypeId[0]));
}
public <R> MethodId<T, R> getMethod(TypeId<R> returnType, String name, TypeId<?>... parameters) {
return new MethodId<>(this, returnType, name, new TypeList(parameters));
}
public String getName() {
return name;
}
@Override
public boolean equals(Object o) {
return o instanceof TypeId
&& ((TypeId) o).name.equals(name);
}
@Override
public int hashCode() {
return name.hashCode();
}
@Override
public String toString() {
return name;
}
}
/*
* Copyright (C) 2011 The Android Open Source Project
*
* 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 com.android.dx;
import com.android.dx.rop.type.StdTypeList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* An immutable of types.
*/
final class TypeList {
final TypeId<?>[] types;
final StdTypeList ropTypes;
TypeList(TypeId<?>[] types) {
this.types = types.clone();
this.ropTypes = new StdTypeList(types.length);
for (int i = 0; i < types.length; i++) {
ropTypes.set(i, types[i].ropType);
}
}
/**
* Returns an immutable list.
*/
public List<TypeId<?>> asList() {
return Collections.unmodifiableList(Arrays.asList(types));
}
@Override
public boolean equals(Object o) {
return o instanceof TypeList && Arrays.equals(((TypeList) o).types, types);
}
@Override
public int hashCode() {
return Arrays.hashCode(types);
}
@Override
public String toString() {
StringBuilder result = new StringBuilder();
for (int i = 0; i < types.length; i++) {
if (i > 0) {
result.append(", ");
}
result.append(types[i]);
}
return result.toString();
}
}
/*
* Copyright (C) 2012 The Android Open Source Project
*
* 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 com.android.dx;
import com.android.dx.rop.code.Rop;
import com.android.dx.rop.code.Rops;
/**
* An operation on one value.
*/
public enum UnaryOp {
/** {@code ~a}. Supports int and long. */
NOT() {
@Override
Rop rop(TypeId<?> type) {
return Rops.opNot(type.ropType);
}
},
/** {@code -a}. Supports int, long, float and double. */
NEGATE() {
@Override
Rop rop(TypeId<?> type) {
return Rops.opNeg(type.ropType);
}
};
abstract Rop rop(TypeId<?> type);
}
...@@ -4,7 +4,7 @@ import android.app.Application; ...@@ -4,7 +4,7 @@ import android.app.Application;
import android.content.Context; import android.content.Context;
import com.swift.sandhook.xposedcompat.classloaders.ComposeClassLoader; import com.swift.sandhook.xposedcompat.classloaders.ComposeClassLoader;
import com.swift.sandhook.xposedcompat.classloaders.XposedClassLoader; import com.swift.sandhook.xposedcompat.methodgen.DynamicBridge;
import com.swift.sandhook.xposedcompat.utils.ApplicationUtils; import com.swift.sandhook.xposedcompat.utils.ApplicationUtils;
import com.swift.sandhook.xposedcompat.utils.ProcessUtils; import com.swift.sandhook.xposedcompat.utils.ProcessUtils;
...@@ -73,4 +73,18 @@ public class XposedCompat { ...@@ -73,4 +73,18 @@ public class XposedCompat {
} }
} }
public static boolean clearCache() {
try {
cacheDir.delete();
cacheDir.mkdirs();
return true;
} catch (Throwable throwable) {
return false;
}
}
public static void clearOatCache() {
DynamicBridge.clearOatFile();
}
} }
package com.swift.sandhook.xposedcompat.methodgen; package com.swift.sandhook.xposedcompat.methodgen;
import android.os.Trace;
import com.swift.sandhook.xposedcompat.XposedCompat; import com.swift.sandhook.xposedcompat.XposedCompat;
import com.swift.sandhook.xposedcompat.utils.DexLog; import com.swift.sandhook.xposedcompat.utils.DexLog;
import com.swift.sandhook.xposedcompat.utils.FileUtils;
import java.io.File; import java.io.File;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
...@@ -21,7 +22,6 @@ public final class DynamicBridge { ...@@ -21,7 +22,6 @@ public final class DynamicBridge {
private static final HookerDexMaker dexMaker = new HookerDexMaker(); private static final HookerDexMaker dexMaker = new HookerDexMaker();
private static final AtomicBoolean dexPathInited = new AtomicBoolean(false); private static final AtomicBoolean dexPathInited = new AtomicBoolean(false);
private static File dexDir; private static File dexDir;
private static File dexOptDir;
public static void onForkPost() { public static void onForkPost() {
dexPathInited.set(false); dexPathInited.set(false);
...@@ -45,28 +45,35 @@ public final class DynamicBridge { ...@@ -45,28 +45,35 @@ public final class DynamicBridge {
// delete previous compiled dex to prevent potential crashing // delete previous compiled dex to prevent potential crashing
// TODO find a way to reuse them in consideration of performance // TODO find a way to reuse them in consideration of performance
try { try {
// we always choose to use device encrypted storage data on android N and later
// in case some app is installing hooks before phone is unlocked
String fixedAppDataDir = XposedCompat.cacheDir.getAbsolutePath(); String fixedAppDataDir = XposedCompat.cacheDir.getAbsolutePath();
dexDir = new File(fixedAppDataDir, "/sandxposed/"); dexDir = new File(fixedAppDataDir, "/sandxposed/");
dexOptDir = new File(dexDir, "oat");
dexDir.mkdirs();
try {
FileUtils.delete(dexOptDir);
} catch (Throwable throwable) {
}
} catch (Throwable throwable) { } catch (Throwable throwable) {
DexLog.e("error when init dex path", throwable); DexLog.e("error when init dex path", throwable);
} }
} }
Trace.beginSection("SandHook-Xposed");
long timeStart = System.currentTimeMillis();
dexMaker.start(hookMethod, additionalHookInfo, dexMaker.start(hookMethod, additionalHookInfo,
XposedCompat.classLoader, dexDir == null ? null : dexDir.getAbsolutePath()); XposedCompat.classLoader, dexDir == null ? null : dexDir.getAbsolutePath());
DexLog.d("hook method <" + hookMethod.toString() + "> use " + (System.currentTimeMillis() - timeStart) + " ms.");
Trace.endSection();
hookedInfo.put(hookMethod, dexMaker.getCallBackupMethod()); hookedInfo.put(hookMethod, dexMaker.getCallBackupMethod());
} catch (Exception e) { } catch (Exception e) {
DexLog.e("error occur when generating dex. dexDir=" + dexDir, e); DexLog.e("error occur when generating dex. dexDir=" + dexDir, e);
} }
} }
public static void clearOatFile() {
String fixedAppDataDir = XposedCompat.cacheDir.getAbsolutePath();
File dexOatDir = new File(fixedAppDataDir, "/sandxposed/oat/");
if (!dexOatDir.exists())
return;
try {
dexOatDir.delete();
} catch (Throwable throwable) {
}
}
private static boolean checkMember(Member member) { private static boolean checkMember(Member member) {
if (member instanceof Method) { if (member instanceof Method) {
......
...@@ -25,6 +25,7 @@ import java.util.concurrent.atomic.AtomicLong; ...@@ -25,6 +25,7 @@ import java.util.concurrent.atomic.AtomicLong;
import de.robv.android.xposed.XC_MethodHook; import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge; import de.robv.android.xposed.XposedBridge;
import static com.swift.sandhook.xposedcompat.utils.DexMakerUtils.MD5;
import static com.swift.sandhook.xposedcompat.utils.DexMakerUtils.autoBoxIfNecessary; import static com.swift.sandhook.xposedcompat.utils.DexMakerUtils.autoBoxIfNecessary;
import static com.swift.sandhook.xposedcompat.utils.DexMakerUtils.autoUnboxIfNecessary; import static com.swift.sandhook.xposedcompat.utils.DexMakerUtils.autoUnboxIfNecessary;
import static com.swift.sandhook.xposedcompat.utils.DexMakerUtils.createResultLocals; import static com.swift.sandhook.xposedcompat.utils.DexMakerUtils.createResultLocals;
...@@ -79,8 +80,6 @@ public class HookerDexMaker { ...@@ -79,8 +80,6 @@ public class HookerDexMaker {
private static final MethodId<XposedBridge, Void> logStrMethodId = private static final MethodId<XposedBridge, Void> logStrMethodId =
xposedBridgeTypeId.getMethod(TypeId.VOID, "log", TypeId.STRING); xposedBridgeTypeId.getMethod(TypeId.VOID, "log", TypeId.STRING);
private static AtomicLong sClassNameSuffix = new AtomicLong(1);
private FieldId<?, XposedBridge.AdditionalHookInfo> mHookInfoFieldId; private FieldId<?, XposedBridge.AdditionalHookInfo> mHookInfoFieldId;
private FieldId<?, Member> mMethodFieldId; private FieldId<?, Member> mMethodFieldId;
private MethodId<?, ?> mBackupMethodId; private MethodId<?, ?> mBackupMethodId;
...@@ -175,15 +174,30 @@ public class HookerDexMaker { ...@@ -175,15 +174,30 @@ public class HookerDexMaker {
} else { } else {
mAppClassLoader = appClassLoader; mAppClassLoader = appClassLoader;
} }
doMake();
}
private void doMake() throws Exception {
mDexMaker = new DexMaker(); mDexMaker = new DexMaker();
// Generate a Hooker class. // Generate a Hooker class.
String className = CLASS_NAME_PREFIX + sClassNameSuffix.getAndIncrement(); String className = getClassName(mMember);
String classDesc = CLASS_DESC_PREFIX + className + ";"; String dexName = className + ".jar";
mHookerTypeId = TypeId.get(classDesc);
HookWrapper.HookEntity hookEntity = null;
//try load cache first
try {
ClassLoader loader = mDexMaker.loadClassDirect(mAppClassLoader, new File(mDexDirPath), dexName);
if (loader != null) {
hookEntity = loadHookerClass(loader, className);
}
} catch (Throwable throwable) {}
//do generate
if (hookEntity == null) {
hookEntity = doMake(className, dexName);
}
SandHook.hook(hookEntity);
}
private HookWrapper.HookEntity doMake(String className, String dexName) throws Exception {
mHookerTypeId = TypeId.get(CLASS_DESC_PREFIX + className + ";");
mDexMaker.declare(mHookerTypeId, className + ".generated", Modifier.PUBLIC, TypeId.OBJECT); mDexMaker.declare(mHookerTypeId, className + ".generated", Modifier.PUBLIC, TypeId.OBJECT);
generateFields(); generateFields();
generateSetupMethod(); generateSetupMethod();
...@@ -195,9 +209,12 @@ public class HookerDexMaker { ...@@ -195,9 +209,12 @@ public class HookerDexMaker {
if (TextUtils.isEmpty(mDexDirPath)) { if (TextUtils.isEmpty(mDexDirPath)) {
throw new IllegalArgumentException("dexDirPath should not be empty!!!"); throw new IllegalArgumentException("dexDirPath should not be empty!!!");
} }
// Create the dex file and load it. // Create the dex file and load it.
loader = mDexMaker.generateAndLoad(mAppClassLoader, new File(mDexDirPath)); loader = mDexMaker.generateAndLoad(mAppClassLoader, new File(mDexDirPath), dexName);
return loadHookerClass(loader, className);
}
private HookWrapper.HookEntity loadHookerClass(ClassLoader loader, String className) throws Exception {
mHookClass = loader.loadClass(className); mHookClass = loader.loadClass(className);
// Execute our newly-generated code in-process. // Execute our newly-generated code in-process.
mHookClass.getMethod(METHOD_NAME_SETUP, Member.class, XposedBridge.AdditionalHookInfo.class) mHookClass.getMethod(METHOD_NAME_SETUP, Member.class, XposedBridge.AdditionalHookInfo.class)
...@@ -205,7 +222,11 @@ public class HookerDexMaker { ...@@ -205,7 +222,11 @@ public class HookerDexMaker {
mHookMethod = mHookClass.getMethod(METHOD_NAME_HOOK, mActualParameterTypes); mHookMethod = mHookClass.getMethod(METHOD_NAME_HOOK, mActualParameterTypes);
mBackupMethod = mHookClass.getMethod(METHOD_NAME_BACKUP, mActualParameterTypes); mBackupMethod = mHookClass.getMethod(METHOD_NAME_BACKUP, mActualParameterTypes);
mCallBackupMethod = mHookClass.getMethod(METHOD_NAME_CALL_BACKUP, mActualParameterTypes); mCallBackupMethod = mHookClass.getMethod(METHOD_NAME_CALL_BACKUP, mActualParameterTypes);
SandHook.hook(new HookWrapper.HookEntity(mMember, mHookMethod, mBackupMethod)); return new HookWrapper.HookEntity(mMember, mHookMethod, mBackupMethod);
}
private String getClassName(Member originMethod) {
return CLASS_NAME_PREFIX + "_" + originMethod.getName() + "_" + MD5(originMethod.toString());
} }
public Method getHookMethod() { public Method getHookMethod() {
......
...@@ -12,6 +12,9 @@ import com.android.dx.rop.type.Type; ...@@ -12,6 +12,9 @@ import com.android.dx.rop.type.Type;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
...@@ -253,4 +256,15 @@ public class DexMakerUtils { ...@@ -253,4 +256,15 @@ public class DexMakerUtils {
return null; return null;
} }
} }
public static String MD5(String source) {
try {
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
messageDigest.update(source.getBytes());
return new BigInteger(1, messageDigest.digest()).toString(32);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return source;
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment