KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > proguard > obfuscate > Obfuscator


1 /*
2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
3  * of Java bytecode.
4  *
5  * Copyright (c) 2002-2007 Eric Lafortune (eric@graphics.cornell.edu)
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by the Free
9  * Software Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  *
13  * This program is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16  * more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program; if not, write to the Free Software Foundation, Inc.,
20  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21  */

22 package proguard.obfuscate;
23
24 import proguard.*;
25 import proguard.classfile.*;
26 import proguard.classfile.constant.visitor.AllConstantVisitor;
27 import proguard.classfile.editor.*;
28 import proguard.classfile.util.*;
29 import proguard.classfile.visitor.*;
30
31 import java.io.*;
32 import java.util.*;
33
34 /**
35  * This class can perform obfuscation of class pools according to a given
36  * specification.
37  *
38  * @author Eric Lafortune
39  */

40 public class Obfuscator
41 {
42     private Configuration configuration;
43
44
45     /**
46      * Creates a new Obfuscator.
47      */

48     public Obfuscator(Configuration configuration)
49     {
50         this.configuration = configuration;
51     }
52
53
54     /**
55      * Performs obfuscation of the given program class pool.
56      */

57     public void execute(ClassPool programClassPool,
58                         ClassPool libraryClassPool) throws IOException
59     {
60         // Check if we have at least some keep commands.
61
if (configuration.keep == null &&
62             configuration.applyMapping == null &&
63             configuration.printMapping == null)
64         {
65             throw new IOException("You have to specify '-keep' options for the obfuscation step.");
66         }
67
68         // Clean up any old visitor info.
69
programClassPool.classesAccept(new ClassCleaner());
70         libraryClassPool.classesAccept(new ClassCleaner());
71
72         // If the class member names have to correspond globally,
73
// link all class members in all classes, otherwise
74
// link all non-private methods in all class hierarchies.
75
ClassVisitor memberInfoLinker =
76             configuration.useUniqueClassMemberNames ?
77                 (ClassVisitor)new AllMemberVisitor(new MethodLinker()) :
78                 (ClassVisitor)new BottomClassFilter(new MethodLinker());
79
80         programClassPool.classesAccept(memberInfoLinker);
81         libraryClassPool.classesAccept(memberInfoLinker);
82
83         // Create a visitor for marking the seeds.
84
NameMarker nameMarker = new NameMarker();
85         ClassPoolVisitor classPoolvisitor =
86             ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.keep,
87                                                                     nameMarker,
88                                                                     nameMarker,
89                                                                     false,
90                                                                     false,
91                                                                     true);
92         // Mark the seeds.
93
programClassPool.accept(classPoolvisitor);
94         libraryClassPool.accept(classPoolvisitor);
95
96         // All library classes and library class members keep their names.
97
libraryClassPool.classesAccept(nameMarker);
98         libraryClassPool.classesAccept(new AllMemberVisitor(nameMarker));
99
100         // Apply the mapping, if one has been specified. The mapping can
101
// override the names of library classes and of library class members.
102
if (configuration.applyMapping != null)
103         {
104             WarningPrinter warningPrinter = configuration.warn ?
105                 new WarningPrinter(System.err) :
106                 null;
107
108             MappingReader reader = new MappingReader(configuration.applyMapping);
109
110             MappingProcessor keeper =
111                 new MultiMappingProcessor(new MappingProcessor[]
112                 {
113                     new MappingKeeper(programClassPool, warningPrinter),
114                     new MappingKeeper(libraryClassPool, null),
115                 });
116
117             reader.pump(keeper);
118
119             if (configuration.warn)
120             {
121                 // Print out a summary of the warnings if necessary.
122
int mappingWarningCount = warningPrinter.getWarningCount();
123                 if (mappingWarningCount > 0)
124                 {
125                     System.err.println("Warning: there were " + mappingWarningCount +
126                                        " kept classes and class members that were remapped anyway.");
127                     System.err.println(" You should adapt your configuration or edit the mapping file.");
128
129                     if (!configuration.ignoreWarnings)
130                     {
131                         System.err.println(" If you are sure this remapping won't hurt,");
132                         System.err.println(" you could try your luck using the '-ignorewarnings' option.");
133                         throw new IOException("Please correct the above warnings first.");
134                     }
135                 }
136             }
137         }
138
139         // Mark attributes that have to be kept.
140
AttributeUsageMarker attributeUsageMarker = new AttributeUsageMarker();
141         if (configuration.keepAttributes != null)
142         {
143             if (!configuration.keepAttributes.isEmpty())
144             {
145                 attributeUsageMarker.setKeepAttributes(configuration.keepAttributes);
146             }
147             else
148             {
149                 attributeUsageMarker.setKeepAllAttributes();
150             }
151         }
152         programClassPool.classesAccept(attributeUsageMarker);
153
154         // Remove the attributes that can be discarded.
155
programClassPool.classesAccept(new AttributeShrinker());
156
157         // Come up with new names for all classes.
158
programClassPool.classesAccept(new ClassObfuscator(programClassPool,
159                                                            configuration.useMixedCaseClassNames,
160                                                            configuration.flattenPackageHierarchy,
161                                                            configuration.repackageClasses,
162                                                            configuration.allowAccessModification));
163
164         // Come up with new names for all class members.
165
NameFactory nameFactory = new SimpleNameFactory();
166
167         if (configuration.obfuscationDictionary != null)
168         {
169             nameFactory = new DictionaryNameFactory(configuration.obfuscationDictionary,
170                                                     nameFactory);
171         }
172
173         WarningPrinter warningPrinter = configuration.warn ?
174             new WarningPrinter(System.err) :
175             null;
176
177         // Maintain a map of names to avoid [descriptor - new name - old name].
178
Map descriptorMap = new HashMap();
179
180         // Do the class member names have to be globally unique?
181
if (configuration.useUniqueClassMemberNames)
182         {
183             // Collect all member names in all classes.
184
programClassPool.classesAccept(
185                 new AllMemberVisitor(
186                 new MemberNameCollector(configuration.overloadAggressively,
187                                         descriptorMap)));
188
189             // Assign new names to all members in all classes.
190
programClassPool.classesAccept(
191                 new AllMemberVisitor(
192                 new MemberObfuscator(configuration.overloadAggressively,
193                                      nameFactory,
194                                      descriptorMap)));
195         }
196         else
197         {
198             // Come up with new names for all non-private class members.
199
programClassPool.classesAccept(
200                 new MultiClassVisitor(new ClassVisitor[]
201                 {
202                     // Collect all private member names in this class and down
203
// the hierarchy.
204
new ClassHierarchyTraveler(true, false, false, true,
205                     new AllMemberVisitor(
206                     new MemberAccessFilter(ClassConstants.INTERNAL_ACC_PRIVATE, 0,
207                     new MemberNameCollector(configuration.overloadAggressively,
208                                             descriptorMap)))),
209
210                     // Collect all non-private member names anywhere in the hierarchy.
211
new ClassHierarchyTraveler(true, true, true, true,
212                     new AllMemberVisitor(
213                     new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE,
214                     new MemberNameCollector(configuration.overloadAggressively,
215                                                 descriptorMap)))),
216
217                     // Assign new names to all non-private members in this class.
218
new AllMemberVisitor(
219                     new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE,
220                     new MemberObfuscator(configuration.overloadAggressively,
221                                          nameFactory,
222                                          descriptorMap))),
223
224                     // Clear the collected names.
225
new MapCleaner(descriptorMap)
226                 }));
227
228             // Come up with new names for all private class members.
229
programClassPool.classesAccept(
230                 new MultiClassVisitor(new ClassVisitor[]
231                 {
232                     // Collect all member names in this class.
233
new AllMemberVisitor(
234                     new MemberNameCollector(configuration.overloadAggressively,
235                                             descriptorMap)),
236
237                     // Collect all non-private member names higher up the hierarchy.
238
new ClassHierarchyTraveler(false, true, true, false,
239                     new AllMemberVisitor(
240                     new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE,
241                     new MemberNameCollector(configuration.overloadAggressively,
242                                             descriptorMap)))),
243
244                     // Assign new names to all private members in this class.
245
new AllMemberVisitor(
246                     new MemberAccessFilter(ClassConstants.INTERNAL_ACC_PRIVATE, 0,
247                     new MemberObfuscator(configuration.overloadAggressively,
248                                          nameFactory,
249                                          descriptorMap))),
250
251                     // Clear the collected names.
252
new MapCleaner(descriptorMap)
253                 }));
254         }
255
256         // Some class members may have ended up with conflicting names.
257
// Come up with new, globally unique names for them.
258
NameFactory specialNameFactory =
259             new SpecialNameFactory(new SimpleNameFactory());
260
261         // Collect a map of special names to avoid
262
// [descriptor - new name - old name].
263
Map specialDescriptorMap = new HashMap();
264
265         programClassPool.classesAccept(
266             new AllMemberVisitor(
267             new MemberSpecialNameFilter(
268             new MemberNameCollector(configuration.overloadAggressively,
269                                     specialDescriptorMap))));
270
271         libraryClassPool.classesAccept(
272             new AllMemberVisitor(
273             new MemberSpecialNameFilter(
274             new MemberNameCollector(configuration.overloadAggressively,
275                                     specialDescriptorMap))));
276
277         // Replace conflicting non-private member names with special names.
278
programClassPool.classesAccept(
279             new MultiClassVisitor(new ClassVisitor[]
280             {
281                 // Collect all private member names in this class and down
282
// the hierarchy.
283
new ClassHierarchyTraveler(true, false, false, true,
284                 new AllMemberVisitor(
285                 new MemberAccessFilter(ClassConstants.INTERNAL_ACC_PRIVATE, 0,
286                 new MemberNameCollector(configuration.overloadAggressively,
287                                         descriptorMap)))),
288
289                 // Collect all non-private member names anywhere in the hierarchy.
290
new ClassHierarchyTraveler(true, true, true, true,
291                 new AllMemberVisitor(
292                 new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE,
293                 new MemberNameCollector(configuration.overloadAggressively,
294                                         descriptorMap)))),
295
296                 // Assign new names to all conflicting non-private members in
297
// this class.
298
new AllMemberVisitor(
299                 new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE,
300                 new MemberNameConflictFixer(configuration.overloadAggressively,
301                                             descriptorMap,
302                                             warningPrinter,
303                 new MemberObfuscator(configuration.overloadAggressively,
304                                      specialNameFactory,
305                                      specialDescriptorMap)))),
306
307                 // Clear the collected names.
308
new MapCleaner(descriptorMap)
309             }));
310
311         // Replace conflicting private member names with special names.
312
// This is only possible if those names were kept or mapped.
313
programClassPool.classesAccept(
314             new MultiClassVisitor(new ClassVisitor[]
315             {
316                 // Collect all member names in this class.
317
new AllMemberVisitor(
318                 new MemberNameCollector(configuration.overloadAggressively,
319                                         descriptorMap)),
320
321                 // Collect all non-private member names higher up the hierarchy.
322
new ClassHierarchyTraveler(false, true, true, false,
323                 new AllMemberVisitor(
324                 new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE,
325                 new MemberNameCollector(configuration.overloadAggressively,
326                                         descriptorMap)))),
327
328                 // Assign new names to all conflicting private members in this
329
// class.
330
new AllMemberVisitor(
331                 new MemberAccessFilter(ClassConstants.INTERNAL_ACC_PRIVATE, 0,
332                 new MemberNameConflictFixer(configuration.overloadAggressively,
333                                             descriptorMap,
334                                             warningPrinter,
335                 new MemberObfuscator(configuration.overloadAggressively,
336                                      specialNameFactory,
337                                      specialDescriptorMap)))),
338
339                 // Clear the collected names.
340
new MapCleaner(descriptorMap)
341             }));
342
343         // Print out any warnings about member name conflicts.
344
if (configuration.warn)
345         {
346             int warningCount = warningPrinter.getWarningCount();
347             if (warningCount > 0)
348             {
349                 System.err.println("Warning: there were " + warningCount +
350                                    " conflicting class member name mappings.");
351                 System.err.println(" Your configuration may be inconsistent.");
352
353                 if (!configuration.ignoreWarnings)
354                 {
355                     System.err.println(" If you are sure the conflicts are harmless,");
356                     System.err.println(" you could try your luck using the '-ignorewarnings' option.");
357                     throw new IOException("Please correct the above warnings first.");
358                 }
359             }
360         }
361
362         // Print out the mapping, if requested.
363
if (configuration.printMapping != null)
364         {
365             PrintStream ps = isFile(configuration.printMapping) ?
366                 new PrintStream(new BufferedOutputStream(new FileOutputStream(configuration.printMapping))) :
367                 System.out;
368
369             // Print out items that will be removed.
370
programClassPool.classesAcceptAlphabetically(new MappingPrinter(ps));
371
372             if (ps != System.out)
373             {
374                 ps.close();
375             }
376         }
377
378         // Actually apply the new names.
379
programClassPool.classesAccept(new ClassRenamer());
380         libraryClassPool.classesAccept(new ClassRenamer());
381
382         // Update all references to these new names.
383
programClassPool.classesAccept(new ClassReferenceFixer(false));
384         libraryClassPool.classesAccept(new ClassReferenceFixer(false));
385         programClassPool.classesAccept(new MemberReferenceFixer());
386
387         // Make package visible elements public or protected, if obfuscated
388
// classes are being repackaged aggressively.
389
if (configuration.repackageClasses != null &&
390             configuration.allowAccessModification)
391         {
392             programClassPool.classesAccept(
393                 new AllConstantVisitor(
394                 new ClassOpener()));
395         }
396
397         // Rename the source file attributes, if requested.
398
if (configuration.newSourceFileAttribute != null)
399         {
400             programClassPool.classesAccept(new SourceFileRenamer(configuration.newSourceFileAttribute));
401         }
402
403         // Mark NameAndType constant pool entries that have to be kept
404
// and remove the other ones.
405
programClassPool.classesAccept(new NameAndTypeUsageMarker());
406         programClassPool.classesAccept(new NameAndTypeShrinker());
407
408         // Mark Utf8 constant pool entries that have to be kept
409
// and remove the other ones.
410
programClassPool.classesAccept(new Utf8UsageMarker());
411         programClassPool.classesAccept(new Utf8Shrinker());
412     }
413
414
415     /**
416      * Returns whether the given file is actually a file, or just a placeholder
417      * for the standard output.
418      */

419     private boolean isFile(File file)
420     {
421         return file.getPath().length() > 0;
422     }
423 }
424
Free Books   Free Magazines  
Popular Tags