1a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler/* 2a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler * Copyright (C) 2016 Google Inc. 3a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler * 4a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler * Licensed under the Apache License, Version 2.0 (the "License"); 5a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler * you may not use this file except in compliance with the License. 6a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler * You may obtain a copy of the License at 7a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler * 8a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler * http://www.apache.org/licenses/LICENSE-2.0 9a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler * 10a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler * Unless required by applicable law or agreed to in writing, software 11a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler * distributed under the License is distributed on an "AS IS" BASIS, 12a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler * See the License for the specific language governing permissions and 14a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler * limitations under the License. 15a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler */ 16a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler 17a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhlerpackage com.android.ahat.proguard; 18a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler 19a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhlerimport java.io.BufferedReader; 20a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhlerimport java.io.File; 21a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhlerimport java.io.FileNotFoundException; 22a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhlerimport java.io.FileReader; 23a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhlerimport java.io.IOException; 24a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhlerimport java.io.Reader; 25a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhlerimport java.text.ParseException; 26a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhlerimport java.util.HashMap; 27a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhlerimport java.util.Map; 28a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler 29b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler/** 30b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * A representation of a proguard mapping for deobfuscating class names, 31b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * field names, and stack frames. 32b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler */ 33a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhlerpublic class ProguardMap { 34a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler 35a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler private static final String ARRAY_SYMBOL = "[]"; 36a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler 37a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler private static class FrameData { 38a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler public FrameData(String clearMethodName, int lineDelta) { 39a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler this.clearMethodName = clearMethodName; 40a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler this.lineDelta = lineDelta; 41a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } 42a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler 43a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler public final String clearMethodName; 44a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler public final int lineDelta; // lineDelta = obfuscatedLine - clearLine 45a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } 46a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler 47a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler private static class ClassData { 48a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler private final String mClearName; 49a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler 50a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler // Mapping from obfuscated field name to clear field name. 51a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler private final Map<String, String> mFields = new HashMap<String, String>(); 52a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler 53a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler // obfuscatedMethodName + clearSignature -> FrameData 54a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler private final Map<String, FrameData> mFrames = new HashMap<String, FrameData>(); 55a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler 56a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler // Constructs a ClassData object for a class with the given clear name. 57a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler public ClassData(String clearName) { 58a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler mClearName = clearName; 59a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } 60a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler 61a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler // Returns the clear name of the class. 62a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler public String getClearName() { 63a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler return mClearName; 64a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } 65a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler 66a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler public void addField(String obfuscatedName, String clearName) { 67a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler mFields.put(obfuscatedName, clearName); 68a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } 69a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler 70a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler // Get the clear name for the field in this class with the given 71a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler // obfuscated name. Returns the original obfuscated name if a clear 72a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler // name for the field could not be determined. 73a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler // TODO: Do we need to take into account the type of the field to 74a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler // propery determine the clear name? 75a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler public String getField(String obfuscatedName) { 76a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler String clearField = mFields.get(obfuscatedName); 77a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler return clearField == null ? obfuscatedName : clearField; 78a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } 79a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler 80a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler // TODO: Does this properly interpret the meaning of line numbers? Is 81a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler // it possible to have multiple frame entries for the same method 82a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler // name and signature that differ only by line ranges? 83a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler public void addFrame(String obfuscatedMethodName, String clearMethodName, 84a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler String clearSignature, int obfuscatedLine, int clearLine) { 85a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler String key = obfuscatedMethodName + clearSignature; 86a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler mFrames.put(key, new FrameData(clearMethodName, obfuscatedLine - clearLine)); 87a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } 88a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler 89a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler public Frame getFrame(String clearClassName, String obfuscatedMethodName, 90a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler String clearSignature, String obfuscatedFilename, int obfuscatedLine) { 91a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler String key = obfuscatedMethodName + clearSignature; 92a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler FrameData frame = mFrames.get(key); 93a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler if (frame == null) { 9488aa6900f62421906af0c3f7a0c245b4f2aef50cRichard Uhler frame = new FrameData(obfuscatedMethodName, 0); 95a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } 96a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler return new Frame(frame.clearMethodName, clearSignature, 9788aa6900f62421906af0c3f7a0c245b4f2aef50cRichard Uhler getFileName(clearClassName), obfuscatedLine - frame.lineDelta); 98a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } 99a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } 100a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler 101a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler private Map<String, ClassData> mClassesFromClearName = new HashMap<String, ClassData>(); 102a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler private Map<String, ClassData> mClassesFromObfuscatedName = new HashMap<String, ClassData>(); 103a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler 104b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler /** 105b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * Information associated with a stack frame that identifies a particular 106b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * line of source code. 107b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler */ 108a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler public static class Frame { 1090a85a95210b3874722a3b36b57fb12307b4b635dRichard Uhler Frame(String method, String signature, String filename, int line) { 110a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler this.method = method; 111a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler this.signature = signature; 112a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler this.filename = filename; 113a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler this.line = line; 114a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } 115a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler 116b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler /** 117b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * The name of the method the stack frame belongs to. 118b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * For example, "equals". 119b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler */ 120a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler public final String method; 121b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler 122b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler /** 123b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * The signature of the method the stack frame belongs to. 124b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * For example, "(Ljava/lang/Object;)Z". 125b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler */ 126a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler public final String signature; 127b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler 128b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler /** 129b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * The name of the file with containing the line of source that the stack 130b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * frame refers to. 131b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler */ 132a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler public final String filename; 133b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler 134b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler /** 135b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * The line number of the code in the source file that the stack frame 136b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * refers to. 137b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler */ 138a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler public final int line; 139a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } 140a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler 141a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler private static void parseException(String msg) throws ParseException { 142a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler throw new ParseException(msg, 0); 143a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } 144a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler 145b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler /** 146b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * Creates a new empty proguard mapping. 147b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * The {@link #readFromFile readFromFile} and 148b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * {@link #readFromReader readFromReader} methods can be used to populate 149b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * the proguard mapping with proguard mapping information. 150b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler */ 151b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler public ProguardMap() { 152b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler } 153b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler 154b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler /** 155b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * Adds the proguard mapping information in <code>mapFile</code> to this 156b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * proguard mapping. 157b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * The <code>mapFile</code> should be a proguard mapping file generated with 158b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * the <code>-printmapping</code> option when proguard was run. 159b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * 160b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * @param mapFile the name of a file with proguard mapping information 161b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * @throws FileNotFoundException If the <code>mapFile</code> could not be 162b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * found 163b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * @throws IOException If an input exception occurred. 164b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * @throws ParseException If the <code>mapFile</code> is not a properly 165b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * formatted proguard mapping file. 166b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler */ 167a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler public void readFromFile(File mapFile) 168a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler throws FileNotFoundException, IOException, ParseException { 169a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler readFromReader(new FileReader(mapFile)); 170a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } 171a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler 172b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler /** 173b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * Adds the proguard mapping information read from <code>mapReader</code> to 174b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * this proguard mapping. 175b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * <code>mapReader</code> should be a Reader of a proguard mapping file 176b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * generated with the <code>-printmapping</code> option when proguard was run. 177b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * 178b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * @param mapReader a Reader for reading the proguard mapping information 179b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * @throws IOException If an input exception occurred. 180b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * @throws ParseException If the <code>mapFile</code> is not a properly 181b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * formatted proguard mapping file. 182b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler */ 183a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler public void readFromReader(Reader mapReader) throws IOException, ParseException { 184a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler BufferedReader reader = new BufferedReader(mapReader); 185a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler String line = reader.readLine(); 186a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler while (line != null) { 187a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler // Class lines are of the form: 188a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler // 'clear.class.name -> obfuscated_class_name:' 189a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler int sep = line.indexOf(" -> "); 190a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler if (sep == -1 || sep + 5 >= line.length()) { 191a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler parseException("Error parsing class line: '" + line + "'"); 192a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } 193a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler String clearClassName = line.substring(0, sep); 194a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler String obfuscatedClassName = line.substring(sep + 4, line.length() - 1); 195a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler 196a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler ClassData classData = new ClassData(clearClassName); 197a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler mClassesFromClearName.put(clearClassName, classData); 198a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler mClassesFromObfuscatedName.put(obfuscatedClassName, classData); 199a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler 200a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler // After the class line comes zero or more field/method lines of the form: 201a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler // ' type clearName -> obfuscatedName' 202a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler line = reader.readLine(); 203a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler while (line != null && line.startsWith(" ")) { 204a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler String trimmed = line.trim(); 205a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler int ws = trimmed.indexOf(' '); 206a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler sep = trimmed.indexOf(" -> "); 207a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler if (ws == -1 || sep == -1) { 208a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler parseException("Error parse field/method line: '" + line + "'"); 209a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } 210a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler 211a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler String type = trimmed.substring(0, ws); 212a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler String clearName = trimmed.substring(ws + 1, sep); 213a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler String obfuscatedName = trimmed.substring(sep + 4, trimmed.length()); 214a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler 215a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler // If the clearName contains '(', then this is for a method instead of a 216a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler // field. 217a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler if (clearName.indexOf('(') == -1) { 218a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler classData.addField(obfuscatedName, clearName); 219a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } else { 220a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler // For methods, the type is of the form: [#:[#:]]<returnType> 221a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler int obfuscatedLine = 0; 222a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler int colon = type.indexOf(':'); 223a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler if (colon != -1) { 224a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler obfuscatedLine = Integer.parseInt(type.substring(0, colon)); 225a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler type = type.substring(colon + 1); 226a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } 227a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler colon = type.indexOf(':'); 228a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler if (colon != -1) { 229a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler type = type.substring(colon + 1); 230a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } 231a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler 232a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler // For methods, the clearName is of the form: <clearName><sig>[:#[:#]] 233a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler int op = clearName.indexOf('('); 234a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler int cp = clearName.indexOf(')'); 235a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler if (op == -1 || cp == -1) { 236a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler parseException("Error parse method line: '" + line + "'"); 237a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } 238a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler 239a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler String sig = clearName.substring(op, cp + 1); 240a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler 241a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler int clearLine = obfuscatedLine; 242a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler colon = clearName.lastIndexOf(':'); 243a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler if (colon != -1) { 244a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler clearLine = Integer.parseInt(clearName.substring(colon + 1)); 245a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler clearName = clearName.substring(0, colon); 246a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } 247a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler 248a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler colon = clearName.lastIndexOf(':'); 249a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler if (colon != -1) { 250a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler clearLine = Integer.parseInt(clearName.substring(colon + 1)); 251a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler clearName = clearName.substring(0, colon); 252a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } 253a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler 254a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler clearName = clearName.substring(0, op); 255a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler 256a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler String clearSig = fromProguardSignature(sig + type); 257a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler classData.addFrame(obfuscatedName, clearName, clearSig, 258a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler obfuscatedLine, clearLine); 259a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } 260a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler 261a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler line = reader.readLine(); 262a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } 263a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } 264a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler reader.close(); 265a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } 266a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler 267b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler /** 268b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * Returns the deobfuscated version of the given obfuscated class name. 269b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * If this proguard mapping does not include information about how to 270b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * deobfuscate the obfuscated class name, the obfuscated class name 271b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * is returned. 272b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * 273b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * @param obfuscatedClassName the obfuscated class name to deobfuscate 274b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * @return the deobfuscated class name. 275b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler */ 276a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler public String getClassName(String obfuscatedClassName) { 277a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler // Class names for arrays may have trailing [] that need to be 278a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler // stripped before doing the lookup. 279a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler String baseName = obfuscatedClassName; 280a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler String arraySuffix = ""; 281a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler while (baseName.endsWith(ARRAY_SYMBOL)) { 282a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler arraySuffix += ARRAY_SYMBOL; 283a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler baseName = baseName.substring(0, baseName.length() - ARRAY_SYMBOL.length()); 284a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } 285a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler 286a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler ClassData classData = mClassesFromObfuscatedName.get(baseName); 287a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler String clearBaseName = classData == null ? baseName : classData.getClearName(); 288a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler return clearBaseName + arraySuffix; 289a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } 290a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler 291b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler /** 292b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * Returns the deobfuscated version of the obfuscated field name for the 293b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * given deobfuscated class name. 294b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * If this proguard mapping does not include information about how to 295b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * deobfuscate the obfuscated field name, the obfuscated field name is 296b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * returned. 297b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * 298b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * @param clearClass the deobfuscated name of the class the field belongs to 299b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * @param obfuscatedField the obfuscated field name to deobfuscate 300b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * @return the deobfuscated field name. 301b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler */ 302a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler public String getFieldName(String clearClass, String obfuscatedField) { 303a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler ClassData classData = mClassesFromClearName.get(clearClass); 304a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler if (classData == null) { 305a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler return obfuscatedField; 306a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } 307a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler return classData.getField(obfuscatedField); 308a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } 309a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler 310b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler /** 311b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * Returns the deobfuscated version of the obfuscated stack frame 312b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * information for the given deobfuscated class name. 313b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * If this proguard mapping does not include information about how to 314b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * deobfuscate the obfuscated stack frame information, the obfuscated stack 315b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * frame information is returned. 316b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * 317b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * @param clearClassName the deobfuscated name of the class the stack frame's 318b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * method belongs to 319b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * @param obfuscatedMethodName the obfuscated method name to deobfuscate 320b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * @param obfuscatedSignature the obfuscated method signature to deobfuscate 321b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * @param obfuscatedFilename the obfuscated file name to deobfuscate. 322b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * @param obfuscatedLine the obfuscated line number to deobfuscate. 323b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler * @return the deobfuscated stack frame information. 324b7732a3fcf06a55075a601dbc593b23a6fc71dbfRichard Uhler */ 325a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler public Frame getFrame(String clearClassName, String obfuscatedMethodName, 326a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler String obfuscatedSignature, String obfuscatedFilename, int obfuscatedLine) { 327a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler String clearSignature = getSignature(obfuscatedSignature); 328a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler ClassData classData = mClassesFromClearName.get(clearClassName); 329a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler if (classData == null) { 330a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler return new Frame(obfuscatedMethodName, clearSignature, 331a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler obfuscatedFilename, obfuscatedLine); 332a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } 333a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler return classData.getFrame(clearClassName, obfuscatedMethodName, clearSignature, 334a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler obfuscatedFilename, obfuscatedLine); 335a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } 336a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler 337a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler // Converts a proguard-formatted method signature into a Java formatted 338a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler // method signature. 339a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler private static String fromProguardSignature(String sig) throws ParseException { 340a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler if (sig.startsWith("(")) { 341a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler int end = sig.indexOf(')'); 342a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler if (end == -1) { 343a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler parseException("Error parsing signature: " + sig); 344a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } 345a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler 346a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler StringBuilder converted = new StringBuilder(); 347a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler converted.append('('); 348a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler if (end > 1) { 349a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler for (String arg : sig.substring(1, end).split(",")) { 350a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler converted.append(fromProguardSignature(arg)); 351a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } 352a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } 353a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler converted.append(')'); 354a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler converted.append(fromProguardSignature(sig.substring(end + 1))); 355a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler return converted.toString(); 356a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } else if (sig.endsWith(ARRAY_SYMBOL)) { 357a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler return "[" + fromProguardSignature(sig.substring(0, sig.length() - 2)); 358a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } else if (sig.equals("boolean")) { 359a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler return "Z"; 360a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } else if (sig.equals("byte")) { 361a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler return "B"; 362a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } else if (sig.equals("char")) { 363a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler return "C"; 364a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } else if (sig.equals("short")) { 365a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler return "S"; 366a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } else if (sig.equals("int")) { 367a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler return "I"; 368a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } else if (sig.equals("long")) { 369a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler return "J"; 370a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } else if (sig.equals("float")) { 371a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler return "F"; 372a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } else if (sig.equals("double")) { 373a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler return "D"; 374a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } else if (sig.equals("void")) { 375a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler return "V"; 376a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } else { 377a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler return "L" + sig.replace('.', '/') + ";"; 378a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } 379a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } 380a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler 381a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler // Return a clear signature for the given obfuscated signature. 382a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler private String getSignature(String obfuscatedSig) { 383a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler StringBuilder builder = new StringBuilder(); 384a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler for (int i = 0; i < obfuscatedSig.length(); i++) { 385a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler if (obfuscatedSig.charAt(i) == 'L') { 386a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler int e = obfuscatedSig.indexOf(';', i); 387a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler builder.append('L'); 388a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler String cls = obfuscatedSig.substring(i + 1, e).replace('/', '.'); 389a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler builder.append(getClassName(cls).replace('.', '/')); 390a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler builder.append(';'); 391a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler i = e; 392a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } else { 393a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler builder.append(obfuscatedSig.charAt(i)); 394a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } 395a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } 396a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler return builder.toString(); 397a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } 398a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler 39988aa6900f62421906af0c3f7a0c245b4f2aef50cRichard Uhler // Return a file name for the given clear class name. 40088aa6900f62421906af0c3f7a0c245b4f2aef50cRichard Uhler private static String getFileName(String clearClass) { 401a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler String filename = clearClass; 40288aa6900f62421906af0c3f7a0c245b4f2aef50cRichard Uhler int dot = filename.lastIndexOf('.'); 403a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler if (dot != -1) { 404a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler filename = filename.substring(dot + 1); 405a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } 406a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler 407a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler int dollar = filename.indexOf('$'); 408a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler if (dollar != -1) { 409a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler filename = filename.substring(0, dollar); 410a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } 411a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler return filename + ".java"; 412a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler } 413a3b7cf0f12da448664536f8b7c793e73da0a4c7fRichard Uhler} 414