1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.ahat;
18 
19 import com.android.ahat.proguard.ProguardMap;
20 import java.io.IOException;
21 import java.io.StringReader;
22 import java.text.ParseException;
23 import org.junit.Test;
24 import static org.junit.Assert.assertEquals;
25 
26 public class ProguardMapTest {
27   private static final String TEST_MAP_FORMAT =
28       "# compiler: richard\n"
29     + "# compiler_version: %s-dev\n"
30     + "# min_api: 10000\n"
31     + "# compiler_hash: b7e25308967a577aa1f05a4b5a745c26\n"
32     + "  # indented comment\n"
33     + "class.that.is.Empty -> a:\n"
34     + "class.that.is.Empty$subclass -> b:\n"
35     + "class.with.only.Fields -> c:\n"
36     + "  # indented inner comment\n"
37     + "    int prim_type_field -> a\n"
38     + "    int[] prim_array_type_field -> b\n"
39     + "    class.that.is.Empty class_type_field -> c\n"
40     + "    class.that.is.Empty[] array_type_field -> d\n"
41     + "    int longObfuscatedNameField -> abc\n"
42     + "class.with.Methods -> d:\n"
43     + "    int some_field -> a\n"
44     + "    12:23:void <clinit>() -> <clinit>\n"
45     + "    42:43:void boringMethod() -> m\n"
46     + "      # indented further inner comment\n"
47     + "    45:48:void methodWithPrimArgs(int,float) -> m\n"
48     + "    49:50:void methodWithPrimArrArgs(int[],float) -> m\n"
49     + "    52:55:void methodWithClearObjArg(class.not.in.Map) -> m\n"
50     + "    57:58:void methodWithClearObjArrArg(class.not.in.Map[]) -> m\n"
51     + "    59:61:void methodWithObfObjArg(class.with.only.Fields) -> m\n"
52     + "    64:66:class.with.only.Fields methodWithObfRes() -> n\n"
53     + "    80:80:void lineObfuscatedMethod():8:8 -> o\n"
54     + "    100:105:void lineObfuscatedMethod():50 -> o\n"
55     + "    90:94:void lineObfuscatedMethod2():9 -> p\n"
56     ;
57 
58   @Test
oldProguardMap()59   public void oldProguardMap() throws IOException, ParseException {
60       runOldProguardMap(String.format(TEST_MAP_FORMAT, "3.0.1"));
61       runOldProguardMap(String.format(TEST_MAP_FORMAT, "3.1"));
62   }
63 
runOldProguardMap(String testMap)64   public void runOldProguardMap(String testMap) throws IOException, ParseException {
65     ProguardMap map = new ProguardMap();
66 
67     // An empty proguard map should not deobfuscate anything.
68     assertEquals("foo.bar.Sludge", map.getClassName("foo.bar.Sludge"));
69     assertEquals("fooBarSludge", map.getClassName("fooBarSludge"));
70     assertEquals("myfield", map.getFieldName("foo.bar.Sludge", "myfield"));
71     assertEquals("myfield", map.getFieldName("fooBarSludge", "myfield"));
72     ProguardMap.Frame frame = map.getFrame(
73         "foo.bar.Sludge", "mymethod", "(Lfoo/bar/Sludge;)V", "SourceFile.java", 123);
74     assertEquals("mymethod", frame.method);
75     assertEquals("(Lfoo/bar/Sludge;)V", frame.signature);
76     assertEquals("SourceFile.java", frame.filename);
77     assertEquals(123, frame.line);
78 
79     // Read in the proguard map.
80     map.readFromReader(new StringReader(testMap));
81 
82     // It should still not deobfuscate things that aren't in the map
83     assertEquals("foo.bar.Sludge", map.getClassName("foo.bar.Sludge"));
84     assertEquals("fooBarSludge", map.getClassName("fooBarSludge"));
85     assertEquals("myfield", map.getFieldName("foo.bar.Sludge", "myfield"));
86     assertEquals("myfield", map.getFieldName("fooBarSludge", "myfield"));
87     frame = map.getFrame("foo.bar.Sludge", "mymethod", "(Lfoo/bar/Sludge;)V",
88         "SourceFile.java", 123);
89     assertEquals("mymethod", frame.method);
90     assertEquals("(Lfoo/bar/Sludge;)V", frame.signature);
91     assertEquals("SourceFile.java", frame.filename);
92     assertEquals(123, frame.line);
93 
94     // Test deobfuscation of class names
95     assertEquals("class.that.is.Empty", map.getClassName("a"));
96     assertEquals("class.that.is.Empty$subclass", map.getClassName("b"));
97     assertEquals("class.with.only.Fields", map.getClassName("c"));
98     assertEquals("class.with.Methods", map.getClassName("d"));
99 
100     // Test deobfuscation of array classes.
101     assertEquals("class.with.Methods[]", map.getClassName("d[]"));
102     assertEquals("class.with.Methods[][]", map.getClassName("d[][]"));
103 
104     // Test deobfuscation of methods
105     assertEquals("prim_type_field", map.getFieldName("class.with.only.Fields", "a"));
106     assertEquals("prim_array_type_field", map.getFieldName("class.with.only.Fields", "b"));
107     assertEquals("class_type_field", map.getFieldName("class.with.only.Fields", "c"));
108     assertEquals("array_type_field", map.getFieldName("class.with.only.Fields", "d"));
109     assertEquals("longObfuscatedNameField", map.getFieldName("class.with.only.Fields", "abc"));
110     assertEquals("some_field", map.getFieldName("class.with.Methods", "a"));
111 
112     // Test deobfuscation of frames
113     frame = map.getFrame("class.with.Methods", "<clinit>", "()V", "SourceFile.java", 13);
114     assertEquals("<clinit>", frame.method);
115     assertEquals("()V", frame.signature);
116     assertEquals("Methods.java", frame.filename);
117     assertEquals(13, frame.line);
118 
119     frame = map.getFrame("class.with.Methods", "m", "()V", "SourceFile.java", 42);
120     assertEquals("boringMethod", frame.method);
121     assertEquals("()V", frame.signature);
122     assertEquals("Methods.java", frame.filename);
123     assertEquals(42, frame.line);
124 
125     frame = map.getFrame("class.with.Methods", "m", "(IF)V", "SourceFile.java", 45);
126     assertEquals("methodWithPrimArgs", frame.method);
127     assertEquals("(IF)V", frame.signature);
128     assertEquals("Methods.java", frame.filename);
129     assertEquals(45, frame.line);
130 
131     frame = map.getFrame("class.with.Methods", "m", "([IF)V", "SourceFile.java", 49);
132     assertEquals("methodWithPrimArrArgs", frame.method);
133     assertEquals("([IF)V", frame.signature);
134     assertEquals("Methods.java", frame.filename);
135     assertEquals(49, frame.line);
136 
137     frame = map.getFrame("class.with.Methods", "m", "(Lclass/not/in/Map;)V",
138         "SourceFile.java", 52);
139     assertEquals("methodWithClearObjArg", frame.method);
140     assertEquals("(Lclass/not/in/Map;)V", frame.signature);
141     assertEquals("Methods.java", frame.filename);
142     assertEquals(52, frame.line);
143 
144     frame = map.getFrame("class.with.Methods", "m", "([Lclass/not/in/Map;)V",
145         "SourceFile.java", 57);
146     assertEquals("methodWithClearObjArrArg", frame.method);
147     assertEquals("([Lclass/not/in/Map;)V", frame.signature);
148     assertEquals("Methods.java", frame.filename);
149     assertEquals(57, frame.line);
150 
151     frame = map.getFrame("class.with.Methods", "m", "(Lc;)V", "SourceFile.java", 59);
152     assertEquals("methodWithObfObjArg", frame.method);
153     assertEquals("(Lclass/with/only/Fields;)V", frame.signature);
154     assertEquals("Methods.java", frame.filename);
155     assertEquals(59, frame.line);
156 
157     frame = map.getFrame("class.with.Methods", "n", "()Lc;", "SourceFile.java", 64);
158     assertEquals("methodWithObfRes", frame.method);
159     assertEquals("()Lclass/with/only/Fields;", frame.signature);
160     assertEquals("Methods.java", frame.filename);
161     assertEquals(64, frame.line);
162 
163     frame = map.getFrame("class.with.Methods", "o", "()V", "SourceFile.java", 80);
164     assertEquals("lineObfuscatedMethod", frame.method);
165     assertEquals("()V", frame.signature);
166     assertEquals("Methods.java", frame.filename);
167     assertEquals(8, frame.line);
168 
169     frame = map.getFrame("class.with.Methods", "o", "()V", "SourceFile.java", 103);
170     assertEquals("lineObfuscatedMethod", frame.method);
171     assertEquals("()V", frame.signature);
172     assertEquals("Methods.java", frame.filename);
173     assertEquals(53, frame.line);
174 
175     frame = map.getFrame("class.with.Methods", "p", "()V", "SourceFile.java", 94);
176     assertEquals("lineObfuscatedMethod2", frame.method);
177     assertEquals("()V", frame.signature);
178     assertEquals("Methods.java", frame.filename);
179     assertEquals(13, frame.line);
180 
181     // Some methods may not have been obfuscated. We should still be able
182     // to compute the filename properly.
183     frame = map.getFrame("class.with.Methods", "unObfuscatedMethodName",
184         "()V", "SourceFile.java", 0);
185     assertEquals("Methods.java", frame.filename);
186   }
187 
188   @Test
proguardMap()189   public void proguardMap() throws IOException, ParseException {
190       runNewProguardMap(String.format(TEST_MAP_FORMAT, "3.1.4"));
191       runNewProguardMap(String.format(TEST_MAP_FORMAT, "3.2"));
192   }
193 
runNewProguardMap(String testMap)194   public void runNewProguardMap(String testMap) throws IOException, ParseException {
195     ProguardMap map = new ProguardMap();
196 
197     // An empty proguard map should not deobfuscate anything.
198     assertEquals("foo.bar.Sludge", map.getClassName("foo.bar.Sludge"));
199     assertEquals("fooBarSludge", map.getClassName("fooBarSludge"));
200     assertEquals("myfield", map.getFieldName("foo.bar.Sludge", "myfield"));
201     assertEquals("myfield", map.getFieldName("fooBarSludge", "myfield"));
202     ProguardMap.Frame frame = map.getFrame(
203         "foo.bar.Sludge", "mymethod", "(Lfoo/bar/Sludge;)V", "SourceFile.java", 123);
204     assertEquals("mymethod", frame.method);
205     assertEquals("(Lfoo/bar/Sludge;)V", frame.signature);
206     assertEquals("SourceFile.java", frame.filename);
207     assertEquals(123, frame.line);
208 
209     // Read in the proguard map.
210     map.readFromReader(new StringReader(testMap));
211 
212     // It should still not deobfuscate things that aren't in the map
213     assertEquals("foo.bar.Sludge", map.getClassName("foo.bar.Sludge"));
214     assertEquals("fooBarSludge", map.getClassName("fooBarSludge"));
215     assertEquals("myfield", map.getFieldName("foo.bar.Sludge", "myfield"));
216     assertEquals("myfield", map.getFieldName("fooBarSludge", "myfield"));
217     frame = map.getFrame("foo.bar.Sludge", "mymethod", "(Lfoo/bar/Sludge;)V",
218         "SourceFile.java", 123);
219     assertEquals("mymethod", frame.method);
220     assertEquals("(Lfoo/bar/Sludge;)V", frame.signature);
221     assertEquals("SourceFile.java", frame.filename);
222     assertEquals(123, frame.line);
223 
224     // Test deobfuscation of class names
225     assertEquals("class.that.is.Empty", map.getClassName("a"));
226     assertEquals("class.that.is.Empty$subclass", map.getClassName("b"));
227     assertEquals("class.with.only.Fields", map.getClassName("c"));
228     assertEquals("class.with.Methods", map.getClassName("d"));
229 
230     // Test deobfuscation of array classes.
231     assertEquals("class.with.Methods[]", map.getClassName("d[]"));
232     assertEquals("class.with.Methods[][]", map.getClassName("d[][]"));
233 
234     // Test deobfuscation of fields
235     assertEquals("prim_type_field", map.getFieldName("class.with.only.Fields", "a"));
236     assertEquals("prim_array_type_field", map.getFieldName("class.with.only.Fields", "b"));
237     assertEquals("class_type_field", map.getFieldName("class.with.only.Fields", "c"));
238     assertEquals("array_type_field", map.getFieldName("class.with.only.Fields", "d"));
239     assertEquals("longObfuscatedNameField", map.getFieldName("class.with.only.Fields", "abc"));
240     assertEquals("some_field", map.getFieldName("class.with.Methods", "a"));
241 
242     // Test deobfuscation of frames
243     frame = map.getFrame("class.with.Methods", "<clinit>", "()V", "SourceFile.java", 13);
244     assertEquals("<clinit>", frame.method);
245     assertEquals("()V", frame.signature);
246     assertEquals("Methods.java", frame.filename);
247     assertEquals(13, frame.line);
248 
249     frame = map.getFrame("class.with.Methods", "m", "()V", "SourceFile.java", 42);
250     assertEquals("boringMethod", frame.method);
251     assertEquals("()V", frame.signature);
252     assertEquals("Methods.java", frame.filename);
253     assertEquals(42, frame.line);
254 
255     frame = map.getFrame("class.with.Methods", "m", "(IF)V", "SourceFile.java", 45);
256     assertEquals("methodWithPrimArgs", frame.method);
257     assertEquals("(IF)V", frame.signature);
258     assertEquals("Methods.java", frame.filename);
259     assertEquals(45, frame.line);
260 
261     frame = map.getFrame("class.with.Methods", "m", "([IF)V", "SourceFile.java", 49);
262     assertEquals("methodWithPrimArrArgs", frame.method);
263     assertEquals("([IF)V", frame.signature);
264     assertEquals("Methods.java", frame.filename);
265     assertEquals(49, frame.line);
266 
267     frame = map.getFrame("class.with.Methods", "m", "(Lclass/not/in/Map;)V",
268         "SourceFile.java", 52);
269     assertEquals("methodWithClearObjArg", frame.method);
270     assertEquals("(Lclass/not/in/Map;)V", frame.signature);
271     assertEquals("Methods.java", frame.filename);
272     assertEquals(52, frame.line);
273 
274     frame = map.getFrame("class.with.Methods", "m", "([Lclass/not/in/Map;)V",
275         "SourceFile.java", 57);
276     assertEquals("methodWithClearObjArrArg", frame.method);
277     assertEquals("([Lclass/not/in/Map;)V", frame.signature);
278     assertEquals("Methods.java", frame.filename);
279     assertEquals(57, frame.line);
280 
281     frame = map.getFrame("class.with.Methods", "m", "(Lc;)V", "SourceFile.java", 59);
282     assertEquals("methodWithObfObjArg", frame.method);
283     assertEquals("(Lclass/with/only/Fields;)V", frame.signature);
284     assertEquals("Methods.java", frame.filename);
285     assertEquals(59, frame.line);
286 
287     frame = map.getFrame("class.with.Methods", "n", "()Lc;", "SourceFile.java", 64);
288     assertEquals("methodWithObfRes", frame.method);
289     assertEquals("()Lclass/with/only/Fields;", frame.signature);
290     assertEquals("Methods.java", frame.filename);
291     assertEquals(64, frame.line);
292 
293     frame = map.getFrame("class.with.Methods", "o", "()V", "SourceFile.java", 80);
294     assertEquals("lineObfuscatedMethod", frame.method);
295     assertEquals("()V", frame.signature);
296     assertEquals("Methods.java", frame.filename);
297     assertEquals(8, frame.line);
298 
299     frame = map.getFrame("class.with.Methods", "o", "()V", "SourceFile.java", 103);
300     assertEquals("lineObfuscatedMethod", frame.method);
301     assertEquals("()V", frame.signature);
302     assertEquals("Methods.java", frame.filename);
303     assertEquals(50, frame.line);
304 
305     frame = map.getFrame("class.with.Methods", "p", "()V", "SourceFile.java", 94);
306     assertEquals("lineObfuscatedMethod2", frame.method);
307     assertEquals("()V", frame.signature);
308     assertEquals("Methods.java", frame.filename);
309     assertEquals(9, frame.line);
310 
311     // Some methods may not have been obfuscated. We should still be able
312     // to compute the filename properly.
313     frame = map.getFrame("class.with.Methods", "unObfuscatedMethodName",
314         "()V", "SourceFile.java", 0);
315     assertEquals("Methods.java", frame.filename);
316   }
317 }
318