From 665884682cda85eb00a4926572277028e41561d3 Mon Sep 17 00:00:00 2001 From: "JesusFreke@JesusFreke.com" Date: Tue, 8 Sep 2009 20:19:28 +0000 Subject: [PATCH] - 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 --- .../java/org/jf/dexlib/Util/Deodexerant.java | 211 ++++++++++++++---- 1 file changed, 162 insertions(+), 49 deletions(-) diff --git a/dexlib/src/main/java/org/jf/dexlib/Util/Deodexerant.java b/dexlib/src/main/java/org/jf/dexlib/Util/Deodexerant.java index 1cb6743f..7384bfa2 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Util/Deodexerant.java +++ b/dexlib/src/main/java/org/jf/dexlib/Util/Deodexerant.java @@ -37,11 +37,16 @@ import java.io.InputStreamReader; import java.util.regex.Pattern; import java.util.regex.Matcher; import java.util.LinkedList; +import java.util.HashMap; +import java.util.List; +import java.util.ArrayList; public class Deodexerant { private final String host; private final int port; + private final HashMap vtableMap = new HashMap(); + public final DexFile dexFile; private Socket socket = null; @@ -77,9 +82,27 @@ public class Deodexerant { throw new RuntimeException("Invalid response from 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); } @@ -99,20 +122,33 @@ public class Deodexerant { return parseAndLookupField(fieldDescriptor); } - public MethodIdItem lookupVirtualMethod(TypeIdItem type, int methodIndex, boolean superLookup) { - connectIfNeeded(); + private ClassData getClassData(TypeIdItem type) { + ClassData classData = vtableMap.get(type); + if (classData == null) { + classData = new ClassData(type); + vtableMap.put(type, classData); + } + return classData; + } - String commandChar = superLookup?"S":"V"; - String response = sendCommand(commandChar + " " + type.getTypeDescriptor() + " " + methodIndex); + public MethodIdItem lookupVirtualMethod(TypeIdItem type, int methodIndex, boolean lookupSuper) { + if (lookupSuper) { + String classType = type.getTypeDescriptor(); - int colon = response.indexOf(':'); - if (colon == -1) { - throw new RuntimeException("Invalid response from deodexerant"); + 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); } - String methodDescriptor = response.substring(colon+2); + ClassData classData = getClassData(type); - return parseAndLookupMethod(methodDescriptor); + return classData.lookupMethod(methodIndex); } public String lookupSuperclass(String typeDescriptor) { @@ -145,6 +181,8 @@ public class Deodexerant { private String sendCommand(String cmd) { try { + connectIfNeeded(); + out.println(cmd); out.flush(); 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 sendMultlineCommand(String cmd) { + try { + connectIfNeeded(); + + out.println(cmd); + out.flush(); + + ArrayList responseLines = new ArrayList(); + 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() { try { if (socket != null) { @@ -173,24 +239,17 @@ public class Deodexerant { } } - private static final Pattern methodPattern = Pattern.compile("(\\[*(?:L[^;]+;|[ZBSCIJFD]))->([^(]+)\\(([^)]*)\\)(.+)"); - private MethodIdItem parseAndLookupMethod(String method) { - //expecting a string like Lsome/class;->someMethod(IIII)Lreturn/type; - - Matcher m = methodPattern.matcher(method); - if (!m.matches()) { - throw new RuntimeException("invalid method string: " + method); + private MethodIdItem parseAndResolveMethod(TypeIdItem classType, String methodName, String methodParams, + String methodRet) { + StringIdItem methodNameItem = StringIdItem.getInternedStringIdItem(dexFile, methodName); + if (methodNameItem == null) { + return null; } - String clazz = m.group(1); - String methodName = m.group(2); - String params = m.group(3); - String ret = m.group(4); - LinkedList paramList = new LinkedList(); - for (int i=0; i 0) { paramListItem = TypeListItem.getInternedTypeListItem(dexFile, paramList); @@ -259,15 +315,12 @@ public class Deodexerant { throw new RuntimeException("Could not find type list item in dex file"); } } - + + TypeIdItem retType = getType(methodRet); + ProtoIdItem protoItem = ProtoIdItem.getInternedProtoIdItem(dexFile, retType, paramListItem); if (protoItem == null) { - throw new RuntimeException("Could not find prototype item in dex file"); - } - - StringIdItem methodNameItem = StringIdItem.getInternedStringIdItem(dexFile, methodName); - if (methodNameItem == null) { - throw new RuntimeException("Could not find method name item in dex file"); + return null; } MethodIdItem methodIdItem; @@ -285,11 +338,13 @@ public class Deodexerant { superclassDescriptor = lookupSuperclass(superclassDescriptor); classType = TypeIdItem.getInternedTypeIdItem(dexFile, superclassDescriptor); } - } while (classType != null); 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 FieldIdItem parseAndLookupField(String field) { //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); if (type == null) { throw new RuntimeException("Could not find type \"" + typeDescriptor + "\" in dex file"); } 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 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; + } + } }