Home | History | Annotate | Download | only in obfuscate
      1 /*
      2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
      3  *             of Java bytecode.
      4  *
      5  * Copyright (c) 2002-2013 Eric Lafortune (eric (at) 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  * This program is distributed in the hope that it will be useful, but WITHOUT
     13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     14  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
     15  * more details.
     16  *
     17  * You should have received a copy of the GNU General Public License along
     18  * with this program; if not, write to the Free Software Foundation, Inc.,
     19  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
     20  */
     21 package proguard.obfuscate;
     22 
     23 import proguard.*;
     24 import proguard.classfile.*;
     25 import proguard.classfile.attribute.visitor.*;
     26 import proguard.classfile.constant.visitor.AllConstantVisitor;
     27 import proguard.classfile.editor.*;
     28 import proguard.classfile.util.*;
     29 import proguard.classfile.visitor.*;
     30 import proguard.util.*;
     31 
     32 import java.io.*;
     33 import java.util.*;
     34 
     35 /**
     36  * This class can perform obfuscation of class pools according to a given
     37  * specification.
     38  *
     39  * @author Eric Lafortune
     40  */
     41 public class Obfuscator
     42 {
     43     private final Configuration configuration;
     44 
     45 
     46     /**
     47      * Creates a new Obfuscator.
     48      */
     49     public Obfuscator(Configuration configuration)
     50     {
     51         this.configuration = configuration;
     52     }
     53 
     54 
     55     /**
     56      * Performs obfuscation of the given program class pool.
     57      */
     58     public void execute(ClassPool programClassPool,
     59                         ClassPool libraryClassPool) throws IOException
     60     {
     61         // Check if we have at least some keep commands.
     62         if (configuration.keep         == null &&
     63             configuration.applyMapping == null &&
     64             configuration.printMapping == null)
     65         {
     66             throw new IOException("You have to specify '-keep' options for the obfuscation step.");
     67         }
     68 
     69         // Clean up any old visitor info.
     70         programClassPool.classesAccept(new ClassCleaner());
     71         libraryClassPool.classesAccept(new ClassCleaner());
     72 
     73         // If the class member names have to correspond globally,
     74         // link all class members in all classes, otherwise
     75         // link all non-private methods in all class hierarchies.
     76         ClassVisitor memberInfoLinker =
     77             configuration.useUniqueClassMemberNames ?
     78                 (ClassVisitor)new AllMemberVisitor(new MethodLinker()) :
     79                 (ClassVisitor)new BottomClassFilter(new MethodLinker());
     80 
     81         programClassPool.classesAccept(memberInfoLinker);
     82         libraryClassPool.classesAccept(memberInfoLinker);
     83 
     84         // Create a visitor for marking the seeds.
     85         NameMarker nameMarker = new NameMarker();
     86         ClassPoolVisitor classPoolvisitor =
     87             ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.keep,
     88                                                                     nameMarker,
     89                                                                     nameMarker,
     90                                                                     false,
     91                                                                     false,
     92                                                                     true);
     93         // Mark the seeds.
     94         programClassPool.accept(classPoolvisitor);
     95         libraryClassPool.accept(classPoolvisitor);
     96 
     97         // All library classes and library class members keep their names.
     98         libraryClassPool.classesAccept(nameMarker);
     99         libraryClassPool.classesAccept(new AllMemberVisitor(nameMarker));
    100 
    101         // Mark attributes that have to be kept.
    102         AttributeVisitor attributeUsageMarker =
    103             new NonEmptyAttributeFilter(
    104             new AttributeUsageMarker());
    105 
    106         AttributeVisitor optionalAttributeUsageMarker =
    107             configuration.keepAttributes == null ? null :
    108                 new AttributeNameFilter(new ListParser(new NameParser()).parse(configuration.keepAttributes),
    109                                         attributeUsageMarker);
    110 
    111         programClassPool.classesAccept(
    112             new AllAttributeVisitor(true,
    113             new RequiredAttributeFilter(attributeUsageMarker,
    114                                         optionalAttributeUsageMarker)));
    115 
    116         // Keep parameter names and types if specified.
    117         if (configuration.keepParameterNames)
    118         {
    119             programClassPool.classesAccept(
    120                 new AllMethodVisitor(
    121                 new MemberNameFilter(
    122                 new AllAttributeVisitor(true,
    123                 new ParameterNameMarker(attributeUsageMarker)))));
    124         }
    125 
    126         // Remove the attributes that can be discarded. Note that the attributes
    127         // may only be discarded after the seeds have been marked, since the
    128         // configuration may rely on annotations.
    129         programClassPool.classesAccept(new AttributeShrinker());
    130 
    131         // Apply the mapping, if one has been specified. The mapping can
    132         // override the names of library classes and of library class members.
    133         if (configuration.applyMapping != null)
    134         {
    135             WarningPrinter warningPrinter = new WarningPrinter(System.err, configuration.warn);
    136 
    137             MappingReader reader = new MappingReader(configuration.applyMapping);
    138 
    139             MappingProcessor keeper =
    140                 new MultiMappingProcessor(new MappingProcessor[]
    141                 {
    142                     new MappingKeeper(programClassPool, warningPrinter),
    143                     new MappingKeeper(libraryClassPool, null),
    144                 });
    145 
    146             reader.pump(keeper);
    147 
    148             // Print out a summary of the warnings if necessary.
    149             int warningCount = warningPrinter.getWarningCount();
    150             if (warningCount > 0)
    151             {
    152                 System.err.println("Warning: there were " + warningCount +
    153                                    " kept classes and class members that were remapped anyway.");
    154                 System.err.println("         You should adapt your configuration or edit the mapping file.");
    155 
    156                 if (!configuration.ignoreWarnings)
    157                 {
    158                     System.err.println("         If you are sure this remapping won't hurt, you could try your luck");
    159                     System.err.println("         using the '-ignorewarnings' option.");
    160                 }
    161 
    162                 System.err.println("         (http://proguard.sourceforge.net/manual/troubleshooting.html#mappingconflict1)");
    163 
    164                 if (!configuration.ignoreWarnings)
    165                 {
    166                     throw new IOException("Please correct the above warnings first.");
    167                 }
    168             }
    169         }
    170 
    171         // Come up with new names for all classes.
    172         DictionaryNameFactory classNameFactory = configuration.classObfuscationDictionary != null ?
    173             new DictionaryNameFactory(configuration.classObfuscationDictionary, null) :
    174             null;
    175 
    176         DictionaryNameFactory packageNameFactory = configuration.packageObfuscationDictionary != null ?
    177             new DictionaryNameFactory(configuration.packageObfuscationDictionary, null) :
    178             null;
    179 
    180         programClassPool.classesAccept(
    181             new ClassObfuscator(programClassPool,
    182                                 classNameFactory,
    183                                 packageNameFactory,
    184                                 configuration.useMixedCaseClassNames,
    185                                 configuration.keepPackageNames,
    186                                 configuration.flattenPackageHierarchy,
    187                                 configuration.repackageClasses,
    188                                 configuration.allowAccessModification));
    189 
    190         // Come up with new names for all class members.
    191         NameFactory nameFactory = new SimpleNameFactory();
    192 
    193         if (configuration.obfuscationDictionary != null)
    194         {
    195             nameFactory = new DictionaryNameFactory(configuration.obfuscationDictionary,
    196                                                     nameFactory);
    197         }
    198 
    199         WarningPrinter warningPrinter = new WarningPrinter(System.err, configuration.warn);
    200 
    201         // Maintain a map of names to avoid [descriptor - new name - old name].
    202         Map descriptorMap = new HashMap();
    203 
    204         // Do the class member names have to be globally unique?
    205         if (configuration.useUniqueClassMemberNames)
    206         {
    207             // Collect all member names in all classes.
    208             programClassPool.classesAccept(
    209                 new AllMemberVisitor(
    210                 new MemberNameCollector(configuration.overloadAggressively,
    211                                         descriptorMap)));
    212 
    213             // Assign new names to all members in all classes.
    214             programClassPool.classesAccept(
    215                 new AllMemberVisitor(
    216                 new MemberObfuscator(configuration.overloadAggressively,
    217                                      nameFactory,
    218                                      descriptorMap)));
    219         }
    220         else
    221         {
    222             // Come up with new names for all non-private class members.
    223             programClassPool.classesAccept(
    224                 new MultiClassVisitor(new ClassVisitor[]
    225                 {
    226                     // Collect all private member names in this class and down
    227                     // the hierarchy.
    228                     new ClassHierarchyTraveler(true, false, false, true,
    229                     new AllMemberVisitor(
    230                     new MemberAccessFilter(ClassConstants.INTERNAL_ACC_PRIVATE, 0,
    231                     new MemberNameCollector(configuration.overloadAggressively,
    232                                             descriptorMap)))),
    233 
    234                     // Collect all non-private member names anywhere in the hierarchy.
    235                     new ClassHierarchyTraveler(true, true, true, true,
    236                     new AllMemberVisitor(
    237                     new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE,
    238                     new MemberNameCollector(configuration.overloadAggressively,
    239                                             descriptorMap)))),
    240 
    241                     // Assign new names to all non-private members in this class.
    242                     new AllMemberVisitor(
    243                     new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE,
    244                     new MemberObfuscator(configuration.overloadAggressively,
    245                                          nameFactory,
    246                                          descriptorMap))),
    247 
    248                     // Clear the collected names.
    249                     new MapCleaner(descriptorMap)
    250                 }));
    251 
    252             // Come up with new names for all private class members.
    253             programClassPool.classesAccept(
    254                 new MultiClassVisitor(new ClassVisitor[]
    255                 {
    256                     // Collect all member names in this class.
    257                     new AllMemberVisitor(
    258                     new MemberNameCollector(configuration.overloadAggressively,
    259                                             descriptorMap)),
    260 
    261                     // Collect all non-private member names higher up the hierarchy.
    262                     new ClassHierarchyTraveler(false, true, true, false,
    263                     new AllMemberVisitor(
    264                     new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE,
    265                     new MemberNameCollector(configuration.overloadAggressively,
    266                                             descriptorMap)))),
    267 
    268                     // Collect all member names from interfaces of abstract
    269                     // classes down the hierarchy.
    270                     // Due to an error in the JLS/JVMS, virtual invocations
    271                     // may end up at a private method otherwise (Sun/Oracle
    272                     // bugs #6691741 and #6684387, ProGuard bug #3471941,
    273                     // and ProGuard test #1180).
    274                     new ClassHierarchyTraveler(false, false, false, true,
    275                     new ClassAccessFilter(ClassConstants.INTERNAL_ACC_ABSTRACT, 0,
    276                     new ClassHierarchyTraveler(false, false, true, false,
    277                     new AllMemberVisitor(
    278                     new MemberNameCollector(configuration.overloadAggressively,
    279                                             descriptorMap))))),
    280 
    281                     // Assign new names to all private members in this class.
    282                     new AllMemberVisitor(
    283                     new MemberAccessFilter(ClassConstants.INTERNAL_ACC_PRIVATE, 0,
    284                     new MemberObfuscator(configuration.overloadAggressively,
    285                                          nameFactory,
    286                                          descriptorMap))),
    287 
    288                     // Clear the collected names.
    289                     new MapCleaner(descriptorMap)
    290                 }));
    291         }
    292 
    293         // Some class members may have ended up with conflicting names.
    294         // Come up with new, globally unique names for them.
    295         NameFactory specialNameFactory =
    296             new SpecialNameFactory(new SimpleNameFactory());
    297 
    298         // Collect a map of special names to avoid
    299         // [descriptor - new name - old name].
    300         Map specialDescriptorMap = new HashMap();
    301 
    302         programClassPool.classesAccept(
    303             new AllMemberVisitor(
    304             new MemberSpecialNameFilter(
    305             new MemberNameCollector(configuration.overloadAggressively,
    306                                     specialDescriptorMap))));
    307 
    308         libraryClassPool.classesAccept(
    309             new AllMemberVisitor(
    310             new MemberSpecialNameFilter(
    311             new MemberNameCollector(configuration.overloadAggressively,
    312                                     specialDescriptorMap))));
    313 
    314         // Replace conflicting non-private member names with special names.
    315         programClassPool.classesAccept(
    316             new MultiClassVisitor(new ClassVisitor[]
    317             {
    318                 // Collect all private member names in this class and down
    319                 // the hierarchy.
    320                 new ClassHierarchyTraveler(true, false, false, true,
    321                 new AllMemberVisitor(
    322                 new MemberAccessFilter(ClassConstants.INTERNAL_ACC_PRIVATE, 0,
    323                 new MemberNameCollector(configuration.overloadAggressively,
    324                                         descriptorMap)))),
    325 
    326                 // Collect all non-private member names in this class and
    327                 // higher up the hierarchy.
    328                 new ClassHierarchyTraveler(true, true, true, false,
    329                 new AllMemberVisitor(
    330                 new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE,
    331                 new MemberNameCollector(configuration.overloadAggressively,
    332                                         descriptorMap)))),
    333 
    334                 // Assign new names to all conflicting non-private members
    335                 // in this class and higher up the hierarchy.
    336                 new ClassHierarchyTraveler(true, true, true, false,
    337                 new AllMemberVisitor(
    338                 new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE,
    339                 new MemberNameConflictFixer(configuration.overloadAggressively,
    340                                             descriptorMap,
    341                                             warningPrinter,
    342                 new MemberObfuscator(configuration.overloadAggressively,
    343                                      specialNameFactory,
    344                                      specialDescriptorMap))))),
    345 
    346                 // Clear the collected names.
    347                 new MapCleaner(descriptorMap)
    348             }));
    349 
    350         // Replace conflicting private member names with special names.
    351         // This is only possible if those names were kept or mapped.
    352         programClassPool.classesAccept(
    353             new MultiClassVisitor(new ClassVisitor[]
    354             {
    355                 // Collect all member names in this class.
    356                 new AllMemberVisitor(
    357                 new MemberNameCollector(configuration.overloadAggressively,
    358                                         descriptorMap)),
    359 
    360                 // Collect all non-private member names higher up the hierarchy.
    361                 new ClassHierarchyTraveler(false, true, true, false,
    362                 new AllMemberVisitor(
    363                 new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE,
    364                 new MemberNameCollector(configuration.overloadAggressively,
    365                                         descriptorMap)))),
    366 
    367                 // Assign new names to all conflicting private members in this
    368                 // class.
    369                 new AllMemberVisitor(
    370                 new MemberAccessFilter(ClassConstants.INTERNAL_ACC_PRIVATE, 0,
    371                 new MemberNameConflictFixer(configuration.overloadAggressively,
    372                                             descriptorMap,
    373                                             warningPrinter,
    374                 new MemberObfuscator(configuration.overloadAggressively,
    375                                      specialNameFactory,
    376                                      specialDescriptorMap)))),
    377 
    378                 // Clear the collected names.
    379                 new MapCleaner(descriptorMap)
    380             }));
    381 
    382         // Print out any warnings about member name conflicts.
    383         int warningCount = warningPrinter.getWarningCount();
    384         if (warningCount > 0)
    385         {
    386             System.err.println("Warning: there were " + warningCount +
    387                                " conflicting class member name mappings.");
    388             System.err.println("         Your configuration may be inconsistent.");
    389 
    390             if (!configuration.ignoreWarnings)
    391             {
    392                 System.err.println("         If you are sure the conflicts are harmless,");
    393                 System.err.println("         you could try your luck using the '-ignorewarnings' option.");
    394                 }
    395 
    396                 System.err.println("         (http://proguard.sourceforge.net/manual/troubleshooting.html#mappingconflict2)");
    397 
    398                 if (!configuration.ignoreWarnings)
    399                 {
    400                 throw new IOException("Please correct the above warnings first.");
    401             }
    402         }
    403 
    404         // Print out the mapping, if requested.
    405         if (configuration.printMapping != null)
    406         {
    407             PrintStream ps =
    408                 configuration.printMapping == Configuration.STD_OUT ? System.out :
    409                     new PrintStream(
    410                     new BufferedOutputStream(
    411                     new FileOutputStream(configuration.printMapping)));
    412 
    413             // Print out items that will be removed.
    414             programClassPool.classesAcceptAlphabetically(new MappingPrinter(ps));
    415 
    416             if (ps == System.out)
    417             {
    418                 ps.flush();
    419             }
    420             else
    421             {
    422                 ps.close();
    423             }
    424         }
    425 
    426         // Actually apply the new names.
    427         programClassPool.classesAccept(new ClassRenamer());
    428         libraryClassPool.classesAccept(new ClassRenamer());
    429 
    430         // Update all references to these new names.
    431         programClassPool.classesAccept(new ClassReferenceFixer(false));
    432         libraryClassPool.classesAccept(new ClassReferenceFixer(false));
    433         programClassPool.classesAccept(new MemberReferenceFixer());
    434 
    435         // Make package visible elements public or protected, if obfuscated
    436         // classes are being repackaged aggressively.
    437         if (configuration.repackageClasses != null &&
    438             configuration.allowAccessModification)
    439         {
    440             programClassPool.classesAccept(
    441                 new AllConstantVisitor(
    442                 new AccessFixer()));
    443 
    444             // Fix the access flags of the inner classes information.
    445             programClassPool.classesAccept(
    446                 new AllAttributeVisitor(
    447                 new AllInnerClassesInfoVisitor(
    448                 new InnerClassesAccessFixer())));
    449         }
    450 
    451         // Fix the bridge method flags.
    452         programClassPool.classesAccept(
    453             new AllMethodVisitor(
    454             new BridgeMethodFixer()));
    455 
    456         // Rename the source file attributes, if requested.
    457         if (configuration.newSourceFileAttribute != null)
    458         {
    459             programClassPool.classesAccept(new SourceFileRenamer(configuration.newSourceFileAttribute));
    460         }
    461 
    462         // Remove unused constants.
    463         programClassPool.classesAccept(
    464             new ConstantPoolShrinker());
    465     }
    466 }
    467