KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > JavaGuard


1 /**
2  * JavaGuard -- an obfuscation package for Java classfiles.
3  *
4  * Copyright (c) 2002 Thorsten Heit (theit@gmx.de)
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  *
20  * The author may be contacted at theit@gmx.de.
21  *
22  *
23  * $Id: JavaGuard.java,v 1.12 2002/06/04 09:59:36 glurk Exp $
24  */

25 import java.io.*;
26 import java.util.Vector JavaDoc;
27 import net.sf.javaguard.*;
28 import net.sf.javaguard.log.*;
29
30
31 /** Obfuscate an input Jar file on the file system into an output Jar file,
32  * using the script file supplied to change obfuscation settings from their
33  * default.
34  * <p />
35  * Default setting are: remove all attributes except 'Code', 'ConstantValue',
36  * 'Exceptions', 'InnerClasses', 'Synthetic'; obfuscate all package, class,
37  * interface, method, field names in the Jar except where this would break
38  * polymorphism from classes and interfaces outside of the Jar.
39  * <p />
40  * Usage: <code>java JavaGuard -i &lt;input-file&gt; -o &lt;output-file&gt; [options]</code>
41  * <p />
42  * where
43  * <ul>
44  * <li><code>&lt;input-file&gt;</code> is the name of the JAR to be obfuscated</li>
45  * <li><code>&lt;output-file&gt;</code> is the name for the obfuscated JAR file</li>
46  * </ul>
47  * and <code>[options]</code> can be
48  * <ul>
49  * <li><code>-s &lt;script-file&gt;</code> to specify a valid JavaGuard script
50  * file</li>
51  * <li><code>-l &lt;log-file&gt;</code> to specify the name for the log file in
52  * which name mappings are listed</li>
53  * </ul>
54  *
55  * The script file may contain comments, directives that specify settings for
56  * the obfuscator and exclusion lines. Comments may be written in shell script
57  * style or C/C++/Java syntax, i.e. using <code>"#"</code> or <code>"//"</code>
58  * for one-line comments and <code>"/*" ... "*&#0047" for multi-line comments.
59  * <p />
60  * Each line in the script file that is not a comment line may contain only
61  * one directive. A directive starts with one of the following keywords and
62  * takes at least one parameter:
63  * <ul>
64  * <li><b><code>.attribute</b> &lt;attr&gt;</code></li>
65  * <li><b><code>.class</b> &lt;regex&gt; [&lt;options&gt;]</code></li>
66  * <li><b><code>.method</b> &lt;regex&gt; [&lt;descriptor&gt;]</code></li>
67  * <li><b><code>.field</b> &lt;regex&gt; [&lt;descriptor&gt;]</code></li>
68  * </ul>
69  * The paramaters for the <code>".attribute"</code> keyword may be
70  * <ul>
71  * <li><code>SourceFile</code> to preserve the 'SourceFile' debugging attribute
72  * of all classes</li>
73  * <li><code>LocalVariableTable</code> to preserve the line numbers if you want
74  * to debug the obfuscated code</li>
75  * <li><code>LocalVariableTable</code> to preserve the 'LocalVariableTable'
76  * debug attribute of all classes</li>
77  * <li><code>rmic</code> to turn on special handling of rmic-generated
78  * classes</li>
79  * <li><code>autocorrect</code></b> to automatically prevent classes that are
80  * used in hardcoded references (such as <code>MyClass.class</code>) from being
81  * assigned obfuscated output names</li>
82  * <li><code>resources</code></b> to automatically rename resource files
83  * according to their obfuscated class file.</li>
84  * </ul>
85  * The &lt;regex&gt; parameter required by the <code>.class</code>,
86  * <code>.method</code> and <code>.field</code> directive may be any
87  * Perl5-compatible regular expression. &lt;descriptor&gt; specifies a method
88  * or field descriptor in Java signature format.
89  * <p />
90  * The <code>.class</code> directive can take the following optional parameters:
91  * <ul>
92  * <li><code>public</code></li>
93  * <li><code>protected</code></li>
94  * <li><code>method</code></li>
95  * <li><code>field</code></li>
96  * </ul>
97  * If the <code>method</code> and/or <code>field</code> parameter are given
98  * without a <code>public</code> or <code>protected</code> modifier
99  * <i>all</i> methods/fields of the specified class(es) are prevented from
100  * being obfuscated.
101  * <p />
102  * If the <code>public</code> and/or <code>protected</code> parameter are
103  * given without a <code>method</code> or <code>field</code> option <i>all</i>
104  * public and/or protected fields and methods are retained.
105  * <p />
106  * If the <code>public</code> and/or <code>protected</code> parameter are
107  * given together with a <code>method</code> or <code>field</code> option
108  * only the public and/or protected fields or methods are retained.
109  *
110  * @see java.lang.Class#getName
111  *
112  * @author <a HREF="mailto:theit@gmx.de">Thorsten Heit</a>
113  */

114 public class JavaGuard {
115   /** Holds a list of input Jar files to obfuscate. */
116   private Vector JavaDoc inputJars;
117   /** Holds a list of input directories with files to obfuscate. The vector
118    * must have exact the same number of elements as the {@link #inputFileFilters}
119    * vector.
120    */

121   private Vector JavaDoc inputDirs;
122   /** Holds a list of file filter expressions for the input directories. The
123    * vector must have exact the same number of elements as the {@link #inputDirs}
124    * vector.
125    */

126   private Vector JavaDoc inputFileFilters;
127   /** Holds the output Jar file. */
128   private File outputFile;
129   /** Holds the script file for the obfuscation. */
130   private File scriptFile;
131   /** Holds the log file. */
132   private File logFile;
133   /** True when the class tree should be dumped before it is obfuscated; false
134    * else.
135    */

136   private boolean dumpClassTree;
137   
138   
139   
140   
141   /** Main method for the obfuscator. Parses the command line parameters and
142    * starts the obfuscation process.
143    * @param args array with command line parameters
144    */

145   public static void main(String JavaDoc args[]) {
146     (new JavaGuard()).start(args);
147   }
148   
149   
150   
151   
152   /** Private constructor.
153    */

154   private JavaGuard() {
155     setInputJars(new Vector JavaDoc());
156     setInputDirs(new Vector JavaDoc());
157     setInputFileFilters(new Vector JavaDoc());
158     setOutputFile(null);
159     setScriptFile(null);
160     setLogFile(null);
161     setDumpClassTree(false);
162   }
163   
164   
165   
166   
167   /** Starts the main application.
168    * @param args the command line parameters
169    */

170   private void start(String JavaDoc[] args) {
171     try {
172       parseParameters(args);
173       verifyParameters();
174     } catch (IllegalArgumentException JavaDoc iex) {
175       ScreenLogger.getInstance().println(iex.getMessage());
176       System.out.println();
177       help();
178     }
179     obfuscate();
180   }
181   
182   
183   
184   
185   /** Parses the command line parameters.
186    * @param args the command line parameters
187    * @throws IllegalArgumentException if there's an error in the command line
188    * arguments
189    */

190   private void parseParameters(String JavaDoc[] args)
191   throws IllegalArgumentException JavaDoc {
192     // Get the arguments, or set their default values
193
if (args.length < 1) help();
194     
195     setDumpClassTree(false);
196     
197     int i = 0;
198     while (i<args.length) {
199       if (args[i].equals("-i") || args[i].equals("--in") || args[i].equals("--input")) {
200         if (i+1 == args.length) throw new IllegalArgumentException JavaDoc("Input JAR file name not specified!");
201         i++;
202         getInputJars().addElement(new File(args[i]));
203       } else if (args[i].equals("-d") || args[i].equals("--dir")) {
204         if (i+1 == args.length) throw new IllegalArgumentException JavaDoc("Input directory not specified!");
205         if (i+2 == args.length) throw new IllegalArgumentException JavaDoc("Regular expression not specified!");
206         i++;
207         getInputDirs().addElement(new File(args[i]));
208         i++;
209         getInputFileFilters().addElement(args[i]);
210       } else if (args[i].equals("-o") || args[i].equals("--out") || args[i].equals("--output")) {
211         if (i+1 == args.length) throw new IllegalArgumentException JavaDoc("Output JAR file name not specified!");
212         i++;
213         setOutputFile(new File(args[i]));
214       } else if (args[i].equals("-s") || args[i].equals("--script")) {
215         if (i+1 == args.length) throw new IllegalArgumentException JavaDoc("Script file name not specified!");
216         i++;
217         setScriptFile(new File(args[i]));
218       } else if (args[i].equals("-l") || args[i].equals("--log")) {
219         if (i+1 == args.length) throw new IllegalArgumentException JavaDoc("Log file name not specified!");
220         i++;
221         setLogFile(new File(args[i]));
222       } else if (args[i].equals("--dump")) {
223         setDumpClassTree(true);
224       } else if (args[i].equals("-v") || args[i].equals("--verbose")) {
225         ScreenLogger.getInstance().incrementLoggingLevel();
226       } else if (args[i].equals("-h") || args[i].equals("--help")) {
227         System.out.println(Version.getProgramName());
228         System.out.println();
229         help();
230       } else if (args[i].equals("--version")) {
231         System.out.println(Version.getProgramName());
232         System.out.println();
233         System.exit(0);
234       } else {
235         throw new IllegalArgumentException JavaDoc("Unknown argument: " + args[i]);
236       }
237       i++;
238     }
239   }
240   
241   
242   
243   
244   /** Verifies that the input parameters are valid. Checks whether the input
245    * files exist and are readable and that the output file is writable.
246    * @throws IllegalArgumentException if there's an error in the parameters
247    */

248   private void verifyParameters()
249   throws IllegalArgumentException JavaDoc {
250     // first check whether we have correct input files/directories
251
if (0 == getInputJars().size() && 0 == getInputDirs().size()) {
252       throw new IllegalArgumentException JavaDoc("No input Jar files and/or input directories given!");
253     }
254     for (int i=0; i<getInputJars().size(); i++) {
255       File inFile = (File) getInputJars().elementAt(i);
256       // Input JAR file must exist and be readable
257
if (!inFile.exists()) {
258         throw new IllegalArgumentException JavaDoc("Input Jar file '"
259         + inFile.getName() + "' does not exist!");
260       }
261       if (!inFile.canRead()) {
262         throw new IllegalArgumentException JavaDoc("Input Jar file '"
263         + inFile.getName() + "' cannot be read!");
264       }
265     }
266     for (int i=0; i<getInputDirs().size(); i++) {
267       File inDir = (File) getInputDirs().elementAt(i);
268       if (!inDir.isDirectory() || !inDir.canRead()) {
269         throw new IllegalArgumentException JavaDoc("Input directory '"
270         + inDir.getName() + "' is not a valid directory!");
271       }
272     }
273     
274     // Check whether the output JAR is writable if it exists
275
if (null == getOutputFile()) {
276       throw new IllegalArgumentException JavaDoc("No output file specified!");
277     }
278     if (getOutputFile().exists() && !getOutputFile().canWrite()) {
279       throw new IllegalArgumentException JavaDoc("The output file is not writable!");
280     }
281     
282     // The script file must be readable if it exists
283
if (null != getScriptFile() && getScriptFile().exists() && !getScriptFile().canRead()) {
284       throw new IllegalArgumentException JavaDoc("Script file is not readable!");
285     }
286     
287     // Logfile must be writable if it exists
288
if (null != getLogFile() && getLogFile().exists() && !getLogFile().canWrite()) {
289       throw new IllegalArgumentException JavaDoc("Logfile is not writable!");
290     }
291   }
292   
293   
294   
295   
296   /** Runs the obfuscator.
297    */

298   private void obfuscate() {
299     // Create the session log file
300
ScreenLogger logger = ScreenLogger.getInstance();
301     logger.println(Version.getProgramName());
302     try {
303       if (null != getLogFile()) {
304         // define the new log file
305
FileLogger.setWriter(new PrintWriter(
306         new BufferedOutputStream(
307         new FileOutputStream(getLogFile()))
308         ));
309       }
310       
311       // Create the name mapping database for the input JAR, constrained by the
312
// options in the rgs script
313
GuardDB db = new GuardDB();
314       // we already verified that the script file exists
315
if (null != getScriptFile()) {
316         logger.println("Reading and parsing script file...");
317         ScriptFile scriptFile = new ScriptFile(getScriptFile());
318         db.setScriptFile(scriptFile);
319       }
320       db.setInput(getInputJars(), getInputDirs(), getInputFileFilters());
321       db.setOutputFile(getOutputFile());
322       db.obfuscate(canDumpClassTree());
323     } catch (Exception JavaDoc ex) {
324       // Log exceptions before exiting
325
logger.println();
326       logger.println("Unrecoverable error during obfuscation:");
327       logger.println(ex.getMessage());
328       logger.printStackTrace(ex);
329       logger.println();
330     } finally {
331       FileLogger.getInstance().close();
332     }
333   }
334   
335   
336   
337   
338   /** Sets the list of input Jar files to obfuscate.
339    * @param vec list of input Jar files
340    * @see #getInputJars
341    */

342   private void setInputJars(Vector JavaDoc vec) {
343     inputJars = vec;
344   }
345   
346   
347   /** Returns the list of input Jar files to obfuscate.
348    * @return list of input Jar files
349    * @see #setInputJars
350    */

351   private Vector JavaDoc getInputJars() {
352     return inputJars;
353   }
354   
355   
356   
357   
358   /** Sets the list of input directories that contain files to obfuscate.
359    * @param vec list of directories
360    * @see #getInputDirs
361    */

362   private void setInputDirs(Vector JavaDoc vec) {
363     inputDirs = vec;
364   }
365   
366   
367   /** Returns the list of input directories that contain files to obfuscate.
368    * @return list of directories
369    * @see #setInputDirs
370    */

371   private Vector JavaDoc getInputDirs() {
372     return inputDirs;
373   }
374   
375   
376   
377   
378   /** Sets the list of file filter expressions for the input directories.
379    * @param vec list of file filter expressions
380    * @see #getInputFileFilters
381    */

382   private void setInputFileFilters(Vector JavaDoc vec) {
383     inputFileFilters = vec;
384   }
385   
386   
387   /** Returns the list of file filter expressions for the input directories.
388    * @return list of file filter expressions
389    * @see #setInputFileFilters
390    */

391   private Vector JavaDoc getInputFileFilters() {
392     return inputFileFilters;
393   }
394   
395   
396   
397   
398   /** Sets the output Jar file.
399    * @param file the output Jar file
400    * @see #getOutputFile
401    */

402   private void setOutputFile(File file) {
403     outputFile = file;
404   }
405   
406   
407   /** Returns the output Jar file.
408    * @return output Jar file
409    * @see #setOutputFile
410    */

411   private File getOutputFile() {
412     return outputFile;
413   }
414   
415   
416   
417   
418   /** Sets the script file for the obfuscation.
419    * @param file the script file for the obfuscation
420    * @see #getScriptFile
421    */

422   private void setScriptFile(File file) {
423     scriptFile = file;
424   }
425   
426   
427   /** Returns the script file for the obfuscation.
428    * @return script file for the obfuscation
429    * @see #setScriptFile
430    */

431   private File getScriptFile() {
432     return scriptFile;
433   }
434   
435   
436   
437   
438   /** Sets the log file.
439    * @param file the log file
440    * @see #getLogFile
441    */

442   private void setLogFile(File file) {
443     logFile = file;
444   }
445   
446   
447   /** Returns the log file.
448    * @return the log file
449    * @see #setLogFile
450    */

451   private File getLogFile() {
452     return logFile;
453   }
454   
455   
456   
457   
458   /** Sets whether the class tree should be dumped before it is obfuscated.
459    * @param dump true when the class tree should be dumped; false else
460    * @see #canDumpClassTree
461    */

462   private void setDumpClassTree(boolean dump) {
463     dumpClassTree = dump;
464   }
465   
466   
467   /** Returns whether the class tree should be dumped before it is obfuscated.
468    * @return true when the class tree should be dumped; false else
469    * @see #setDumpClassTree
470    */

471   private boolean canDumpClassTree() {
472     return dumpClassTree;
473   }
474   
475   
476   
477   
478   /** Prints the usage and a short description of the command line arguments on
479    * the system console.
480    */

481   public static void help() {
482     System.out.println("Usage: java JavaGuard -i <input-file> -o <output-file> [options]");
483     System.out.println();
484     System.out.println("-i <input-file> The name of an input JAR file to be obfuscated");
485     System.out.println("--input");
486     System.out.println();
487     System.out.println("-d <dir> <regex> Obfuscate all files below the directory that match the regular");
488     System.out.println("--dir expression");
489     System.out.println();
490     System.out.println("-o <output-file> The name of the output file that will contain the obfuscated");
491     System.out.println("--output contents of all input files");
492     System.out.println();
493     System.out.println("-s <script> The name of a valid JavaGuard script file");
494     System.out.println("--script");
495     System.out.println();
496     System.out.println("-l <log-file> The name for the log file");
497     System.out.println("--log");
498     System.out.println();
499     System.out.println("--dump Dump the parsed class tree before obfuscation.");
500     System.out.println();
501     System.out.println("-v Increment the logging level. To be more verbose specify the");
502     System.out.println("--verbose parameter several times.");
503     System.out.println();
504     System.out.println("-h Show this info page.");
505     System.out.println("--help");
506     System.out.println();
507     System.out.println("--version Show the program version.");
508     System.out.println();
509     System.exit(-1);
510   }
511 }
512
Popular Tags