mirror of
https://github.com/revanced/smali.git
synced 2025-06-01 05:00:12 +02:00
- Add support for the change in deodexerant where it now dumps all vtable entries, instead of having to query for a specific one
- cache the vtable information we get from deodexerant git-svn-id: https://smali.googlecode.com/svn/trunk@440 55b6fa8a-2a1e-11de-a435-ffa8d773f76a
This commit is contained in:
parent
4ef9fbbd21
commit
665884682c
@ -37,11 +37,16 @@ import java.io.InputStreamReader;
|
|||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
public class Deodexerant {
|
public class Deodexerant {
|
||||||
private final String host;
|
private final String host;
|
||||||
private final int port;
|
private final int port;
|
||||||
|
|
||||||
|
private final HashMap<TypeIdItem, ClassData> vtableMap = new HashMap<TypeIdItem, ClassData>();
|
||||||
|
|
||||||
public final DexFile dexFile;
|
public final DexFile dexFile;
|
||||||
|
|
||||||
private Socket socket = null;
|
private Socket socket = null;
|
||||||
@ -79,7 +84,25 @@ public class Deodexerant {
|
|||||||
|
|
||||||
String methodDescriptor = response.substring(colon+2);
|
String methodDescriptor = response.substring(colon+2);
|
||||||
|
|
||||||
MethodIdItem method = parseAndLookupMethod(methodDescriptor);
|
Matcher m = fullMethodPattern.matcher(methodDescriptor);
|
||||||
|
if (!m.matches()) {
|
||||||
|
throw new RuntimeException("Invalid method descriptor: " + methodDescriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
String classType = m.group(1);
|
||||||
|
String methodName = m.group(2);
|
||||||
|
String methodParams = m.group(3);
|
||||||
|
String methodRet = m.group(4);
|
||||||
|
|
||||||
|
TypeIdItem classTypeItem = TypeIdItem.getInternedTypeIdItem(dexFile, classType);
|
||||||
|
if (classTypeItem == null) {
|
||||||
|
throw new RuntimeException("Could not find type " + classType + " in the dex file");
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodIdItem method = parseAndResolveMethod(classTypeItem, methodName, methodParams, methodRet);
|
||||||
|
if (method == null) {
|
||||||
|
throw new RuntimeException("Could not find method " + methodDescriptor + " in the dex file");
|
||||||
|
}
|
||||||
|
|
||||||
return new InlineMethod(method, type);
|
return new InlineMethod(method, type);
|
||||||
}
|
}
|
||||||
@ -99,20 +122,33 @@ public class Deodexerant {
|
|||||||
return parseAndLookupField(fieldDescriptor);
|
return parseAndLookupField(fieldDescriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MethodIdItem lookupVirtualMethod(TypeIdItem type, int methodIndex, boolean superLookup) {
|
private ClassData getClassData(TypeIdItem type) {
|
||||||
connectIfNeeded();
|
ClassData classData = vtableMap.get(type);
|
||||||
|
if (classData == null) {
|
||||||
String commandChar = superLookup?"S":"V";
|
classData = new ClassData(type);
|
||||||
String response = sendCommand(commandChar + " " + type.getTypeDescriptor() + " " + methodIndex);
|
vtableMap.put(type, classData);
|
||||||
|
}
|
||||||
int colon = response.indexOf(':');
|
return classData;
|
||||||
if (colon == -1) {
|
|
||||||
throw new RuntimeException("Invalid response from deodexerant");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String methodDescriptor = response.substring(colon+2);
|
public MethodIdItem lookupVirtualMethod(TypeIdItem type, int methodIndex, boolean lookupSuper) {
|
||||||
|
if (lookupSuper) {
|
||||||
|
String classType = type.getTypeDescriptor();
|
||||||
|
|
||||||
return parseAndLookupMethod(methodDescriptor);
|
do
|
||||||
|
{
|
||||||
|
classType = lookupSuperclass(type.getTypeDescriptor());
|
||||||
|
if (classType == null) {
|
||||||
|
throw new RuntimeException("Could not find any superclass for type " + type.getTypeDescriptor() +
|
||||||
|
" in the dex file");
|
||||||
|
}
|
||||||
|
type = TypeIdItem.getInternedTypeIdItem(dexFile, classType);
|
||||||
|
} while (type == null);
|
||||||
|
}
|
||||||
|
|
||||||
|
ClassData classData = getClassData(type);
|
||||||
|
|
||||||
|
return classData.lookupMethod(methodIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String lookupSuperclass(String typeDescriptor) {
|
public String lookupSuperclass(String typeDescriptor) {
|
||||||
@ -145,6 +181,8 @@ public class Deodexerant {
|
|||||||
|
|
||||||
private String sendCommand(String cmd) {
|
private String sendCommand(String cmd) {
|
||||||
try {
|
try {
|
||||||
|
connectIfNeeded();
|
||||||
|
|
||||||
out.println(cmd);
|
out.println(cmd);
|
||||||
out.flush();
|
out.flush();
|
||||||
String response = in.readLine();
|
String response = in.readLine();
|
||||||
@ -158,6 +196,34 @@ public class Deodexerant {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//The command is still just a single line, but we're expecting a multi-line
|
||||||
|
//response. The repsonse is considered finished when a line starting with "err"
|
||||||
|
//or with "done" is encountered
|
||||||
|
private List<String> sendMultlineCommand(String cmd) {
|
||||||
|
try {
|
||||||
|
connectIfNeeded();
|
||||||
|
|
||||||
|
out.println(cmd);
|
||||||
|
out.flush();
|
||||||
|
|
||||||
|
ArrayList<String> responseLines = new ArrayList<String>();
|
||||||
|
String response = in.readLine();
|
||||||
|
while (!response.startsWith("done"))
|
||||||
|
{
|
||||||
|
if (response.startsWith("err")) {
|
||||||
|
throw new RuntimeException(response.substring(5));
|
||||||
|
}
|
||||||
|
|
||||||
|
responseLines.add(response);
|
||||||
|
response = in.readLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
return responseLines;
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void connectIfNeeded() {
|
private void connectIfNeeded() {
|
||||||
try {
|
try {
|
||||||
if (socket != null) {
|
if (socket != null) {
|
||||||
@ -173,24 +239,17 @@ public class Deodexerant {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Pattern methodPattern = Pattern.compile("(\\[*(?:L[^;]+;|[ZBSCIJFD]))->([^(]+)\\(([^)]*)\\)(.+)");
|
private MethodIdItem parseAndResolveMethod(TypeIdItem classType, String methodName, String methodParams,
|
||||||
private MethodIdItem parseAndLookupMethod(String method) {
|
String methodRet) {
|
||||||
//expecting a string like Lsome/class;->someMethod(IIII)Lreturn/type;
|
StringIdItem methodNameItem = StringIdItem.getInternedStringIdItem(dexFile, methodName);
|
||||||
|
if (methodNameItem == null) {
|
||||||
Matcher m = methodPattern.matcher(method);
|
return null;
|
||||||
if (!m.matches()) {
|
|
||||||
throw new RuntimeException("invalid method string: " + method);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String clazz = m.group(1);
|
|
||||||
String methodName = m.group(2);
|
|
||||||
String params = m.group(3);
|
|
||||||
String ret = m.group(4);
|
|
||||||
|
|
||||||
LinkedList<TypeIdItem> paramList = new LinkedList<TypeIdItem>();
|
LinkedList<TypeIdItem> paramList = new LinkedList<TypeIdItem>();
|
||||||
|
|
||||||
for (int i=0; i<params.length(); i++) {
|
for (int i=0; i<methodParams.length(); i++) {
|
||||||
switch (params.charAt(i)) {
|
switch (methodParams.charAt(i)) {
|
||||||
case 'Z':
|
case 'Z':
|
||||||
case 'B':
|
case 'B':
|
||||||
case 'S':
|
case 'S':
|
||||||
@ -199,16 +258,16 @@ public class Deodexerant {
|
|||||||
case 'J':
|
case 'J':
|
||||||
case 'F':
|
case 'F':
|
||||||
case 'D':
|
case 'D':
|
||||||
paramList.add(getType(dexFile, Character.toString(params.charAt(i))));
|
paramList.add(getType(Character.toString(methodParams.charAt(i))));
|
||||||
break;
|
break;
|
||||||
case 'L':
|
case 'L':
|
||||||
{
|
{
|
||||||
int end = params.indexOf(';', i);
|
int end = methodParams.indexOf(';', i);
|
||||||
if (end == -1) {
|
if (end == -1) {
|
||||||
throw new RuntimeException("invalid parameter in the method string: " + method);
|
throw new RuntimeException("invalid parameter in the method");
|
||||||
}
|
}
|
||||||
|
|
||||||
paramList.add(getType(dexFile, params.substring(i, end+1)));
|
paramList.add(getType(methodParams.substring(i, end+1)));
|
||||||
i = end;
|
i = end;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -216,10 +275,10 @@ public class Deodexerant {
|
|||||||
{
|
{
|
||||||
int end;
|
int end;
|
||||||
int typeStart = i+1;
|
int typeStart = i+1;
|
||||||
while (typeStart < params.length() && params.charAt(typeStart) == '[') {
|
while (typeStart < methodParams.length() && methodParams.charAt(typeStart) == '[') {
|
||||||
typeStart++;
|
typeStart++;
|
||||||
}
|
}
|
||||||
switch (params.charAt(typeStart)) {
|
switch (methodParams.charAt(typeStart)) {
|
||||||
case 'Z':
|
case 'Z':
|
||||||
case 'B':
|
case 'B':
|
||||||
case 'S':
|
case 'S':
|
||||||
@ -231,27 +290,24 @@ public class Deodexerant {
|
|||||||
end = typeStart;
|
end = typeStart;
|
||||||
break;
|
break;
|
||||||
case 'L':
|
case 'L':
|
||||||
end = params.indexOf(';', typeStart);
|
end = methodParams.indexOf(';', typeStart);
|
||||||
if (end == -1) {
|
if (end == -1) {
|
||||||
throw new RuntimeException("invalid parameter in the method string: " + method);
|
throw new RuntimeException("invalid parameter in the method");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new RuntimeException("invalid parameter in the method string: " + method);
|
throw new RuntimeException("invalid parameter in the method");
|
||||||
}
|
}
|
||||||
|
|
||||||
paramList.add(getType(dexFile, params.substring(i, end+1)));
|
paramList.add(getType(methodParams.substring(i, end+1)));
|
||||||
i = end;
|
i = end;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
throw new RuntimeException("invalid parameter in the method string: " + method);
|
throw new RuntimeException("invalid parameter in the method");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeIdItem classType = getType(dexFile, clazz);
|
|
||||||
TypeIdItem retType = getType(dexFile, ret);
|
|
||||||
|
|
||||||
TypeListItem paramListItem = null;
|
TypeListItem paramListItem = null;
|
||||||
if (paramList.size() > 0) {
|
if (paramList.size() > 0) {
|
||||||
paramListItem = TypeListItem.getInternedTypeListItem(dexFile, paramList);
|
paramListItem = TypeListItem.getInternedTypeListItem(dexFile, paramList);
|
||||||
@ -260,14 +316,11 @@ public class Deodexerant {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TypeIdItem retType = getType(methodRet);
|
||||||
|
|
||||||
ProtoIdItem protoItem = ProtoIdItem.getInternedProtoIdItem(dexFile, retType, paramListItem);
|
ProtoIdItem protoItem = ProtoIdItem.getInternedProtoIdItem(dexFile, retType, paramListItem);
|
||||||
if (protoItem == null) {
|
if (protoItem == null) {
|
||||||
throw new RuntimeException("Could not find prototype item in dex file");
|
return null;
|
||||||
}
|
|
||||||
|
|
||||||
StringIdItem methodNameItem = StringIdItem.getInternedStringIdItem(dexFile, methodName);
|
|
||||||
if (methodNameItem == null) {
|
|
||||||
throw new RuntimeException("Could not find method name item in dex file");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MethodIdItem methodIdItem;
|
MethodIdItem methodIdItem;
|
||||||
@ -285,11 +338,13 @@ public class Deodexerant {
|
|||||||
superclassDescriptor = lookupSuperclass(superclassDescriptor);
|
superclassDescriptor = lookupSuperclass(superclassDescriptor);
|
||||||
classType = TypeIdItem.getInternedTypeIdItem(dexFile, superclassDescriptor);
|
classType = TypeIdItem.getInternedTypeIdItem(dexFile, superclassDescriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
} while (classType != null);
|
} while (classType != null);
|
||||||
throw new RuntimeException("Could not find method in dex file");
|
throw new RuntimeException("Could not find method in dex file");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final Pattern fullMethodPattern = Pattern.compile("(\\[*(?:L[^;]+;|[ZBSCIJFD]))->([^(]+)\\(([^)]*)\\)(.+)");
|
||||||
|
private static final Pattern shortMethodPattern = Pattern.compile("([^(]+)\\(([^)]*)\\)(.+)");
|
||||||
|
|
||||||
private static final Pattern fieldPattern = Pattern.compile("(\\[*L[^;]+;)->([^:]+):(.+)");
|
private static final Pattern fieldPattern = Pattern.compile("(\\[*L[^;]+;)->([^:]+):(.+)");
|
||||||
private FieldIdItem parseAndLookupField(String field) {
|
private FieldIdItem parseAndLookupField(String field) {
|
||||||
//expecting a string like Lsome/class;->someField:Lfield/type;
|
//expecting a string like Lsome/class;->someField:Lfield/type;
|
||||||
@ -342,11 +397,69 @@ public class Deodexerant {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static TypeIdItem getType(DexFile dexFile, String typeDescriptor) {
|
private TypeIdItem getType(String typeDescriptor) {
|
||||||
TypeIdItem type = TypeIdItem.getInternedTypeIdItem(dexFile, typeDescriptor);
|
TypeIdItem type = TypeIdItem.getInternedTypeIdItem(dexFile, typeDescriptor);
|
||||||
if (type == null) {
|
if (type == null) {
|
||||||
throw new RuntimeException("Could not find type \"" + typeDescriptor + "\" in dex file");
|
throw new RuntimeException("Could not find type \"" + typeDescriptor + "\" in dex file");
|
||||||
}
|
}
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private class ClassData {
|
||||||
|
public final TypeIdItem ClassType;
|
||||||
|
|
||||||
|
public boolean vtableLoaded;
|
||||||
|
public String[] MethodNames;
|
||||||
|
public String[] MethodParams;
|
||||||
|
public String[] MethodRets;
|
||||||
|
public MethodIdItem[] resolvedMethods;
|
||||||
|
|
||||||
|
|
||||||
|
public ClassData(TypeIdItem classType) {
|
||||||
|
this.ClassType = classType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MethodIdItem lookupMethod(int index) {
|
||||||
|
if (!vtableLoaded) {
|
||||||
|
loadvtable();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resolvedMethods[index] == null) {
|
||||||
|
resolvedMethods[index] = parseAndResolveMethod(ClassType, MethodNames[index], MethodParams[index],
|
||||||
|
MethodRets[index]);
|
||||||
|
}
|
||||||
|
return resolvedMethods[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadvtable() {
|
||||||
|
List<String> responseLines = sendMultlineCommand("V " + ClassType.getTypeDescriptor());
|
||||||
|
|
||||||
|
MethodNames = new String[responseLines.size()];
|
||||||
|
MethodParams = new String[responseLines.size()];
|
||||||
|
MethodRets = new String[responseLines.size()];
|
||||||
|
resolvedMethods = new MethodIdItem[responseLines.size()];
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
for (String vtableEntry: responseLines) {
|
||||||
|
if (!vtableEntry.startsWith("vtable: ")) {
|
||||||
|
throw new RuntimeException("Invalid response from deodexerant");
|
||||||
|
}
|
||||||
|
|
||||||
|
String method = vtableEntry.substring(8);
|
||||||
|
Matcher m = shortMethodPattern.matcher(method);
|
||||||
|
if (!m.matches()) {
|
||||||
|
throw new RuntimeException("invalid method string: " + method);
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodNames[index] = m.group(1);
|
||||||
|
MethodParams[index] = m.group(2);
|
||||||
|
MethodRets[index] = m.group(3);
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
vtableLoaded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user