001: /* ===========================================================================
002: * $RCSfile: RetroGuardImpl.java,v $
003: * ===========================================================================
004: *
005: * RetroGuard -- an obfuscation package for Java classfiles.
006: *
007: * Copyright (c) 1998-2006 Mark Welsh (markw@retrologic.com)
008: *
009: * This program can be redistributed and/or modified under the terms of the
010: * Version 2 of the GNU General Public License as published by the Free
011: * Software Foundation.
012: *
013: * This program is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016: * GNU General Public License for more details.
017: *
018: */
019:
020: package COM.rl.obf;
021:
022: import java.io.*;
023: import java.util.*;
024: import COM.rl.obf.classfile.ClassFile;
025: import COM.rl.obf.gui.*;
026:
027: /**
028: * Main implementation class for obfuscator package.
029: *
030: * @author Mark Welsh
031: */
032: public class RetroGuardImpl {
033: // Constants -------------------------------------------------------------
034: public static final String DEFAULT_IN_FILE_NAME = "in.jar";
035: public static final String DEFAULT_OUT_FILE_NAME = "out.jar";
036: public static final String DEFAULT_RGS_FILE_NAME = "script.rgs";
037: public static final String DEFAULT_LOG_FILE_NAME = "retroguard.log";
038: private static final String LOG_TITLE_PRE_VERSION = "# RetroGuard v";
039: private static final String LOG_TITLE_POST_VERSION = ", by Retrologic Systems - www.retrologic.com";
040: private static final String LOG_CREATED = "# Logfile created on ";
041: private static final String LOG_INPUT_FILE = "# Jar file to be obfuscated: ";
042: private static final String LOG_OUTPUT_FILE = "# Target Jar file for obfuscated code: ";
043: private static final String LOG_SCRIPT_FILE = "# RetroGuard Script file used: ";
044: private static final String LOG_NO_SCRIPT = "(none, defaults used)";
045: private static final String LOG_ERROR = "# Unrecoverable error during obfuscation:";
046: private static final String LOG_ZIP_ERROR = "# Review input jar for duplicate classes (same classfile with two different filenames).";
047: private static final String SEE_LOG_FILE = "Unrecoverable error during obfuscation, see log file for details.";
048:
049: // Fields ----------------------------------------------------------------
050: private File inFile;
051: private File outFile;
052: private File rgsFile;
053: private File logFile;
054:
055: // Class Methods ---------------------------------------------------------
056: /**
057: * Main entry point for the obfuscator.
058: *
059: * @param inFilename a readable input JAR file name
060: * @param outFilename a writable JAR file name for obfuscated output
061: * @param rgsFilename valid RetroGuard Script data file name, or null (which implies default settings)
062: * @param logFilename name for the log data file
063: */
064: public static void obfuscate(String inFilename, String outFilename,
065: String rgsFilename, String logFilename) throws Exception {
066: File inFile = new File(
067: inFilename == null ? DEFAULT_IN_FILE_NAME : inFilename);
068: File outFile = new File(
069: outFilename == null ? DEFAULT_OUT_FILE_NAME
070: : outFilename);
071: File rgsFile = new File(
072: rgsFilename == null ? DEFAULT_RGS_FILE_NAME
073: : rgsFilename);
074: File logFile = new File(
075: logFilename == null ? DEFAULT_LOG_FILE_NAME
076: : logFilename);
077:
078: // Input JAR file must exist and be readable
079: if (!inFile.exists()) {
080: throw new IllegalArgumentException(
081: "JAR specified for obfuscation does not exist.");
082: }
083: if (!inFile.canRead()) {
084: throw new IllegalArgumentException(
085: "JAR specified for obfuscation exists but cannot be read.");
086: }
087:
088: // Output JAR file must be writable if it exists
089: if (outFile.exists() && !outFile.canWrite()) {
090: throw new IllegalArgumentException(
091: "Output JAR file cannot be written to.");
092: }
093:
094: // Script file must be readable if it exists, but need not exist
095: // (revert to default obfuscation settings in that case)
096: if (rgsFile.exists()) {
097: if (!rgsFile.canRead()) {
098: throw new IllegalArgumentException(
099: "Script file exists but cannot be read.");
100: }
101: }
102:
103: // Logfile must be writable if it exists
104: if (logFile.exists() && !logFile.canWrite()) {
105: throw new IllegalArgumentException(
106: "Logfile cannot be written to.");
107: }
108:
109: // Call the main entry point on the obfuscator.
110: RetroGuardImpl.obfuscate(inFile, outFile, rgsFile, logFile);
111: }
112:
113: /**
114: * Main entry point for the obfuscator.
115: *
116: * @param inFile a File pointing to a readable JAR
117: * @param outFile a writable JAR for obfuscated output
118: * @param rgsFile valid RetroGuard Script data, or null (which implies default settings)
119: * @param logFile file for the log data.
120: */
121: public static void obfuscate(File inFile, File outFile,
122: File rgsFile, File logFile) throws Exception {
123: new RetroGuardImpl(inFile, outFile, rgsFile, logFile).run();
124: }
125:
126: // Instance Methods ------------------------------------------------------
127: // Private constructor takes in-jar, out-jar and script specifiers.
128: private RetroGuardImpl(File inFile, File outFile, File rgsFile,
129: File logFile) {
130: this .inFile = inFile;
131: this .outFile = outFile;
132: this .rgsFile = rgsFile;
133: this .logFile = logFile;
134: }
135:
136: // Run the obfuscator.
137: private void run() throws Exception {
138: // Create the session log file
139: PrintWriter log = new PrintWriter(new BufferedOutputStream(
140: new FileOutputStream(logFile)));
141: try {
142: // Write out the log header
143: writeLogHeader(log);
144:
145: // Create the name mapping database for the input JAR, constrained
146: // by the options in the rgs script
147: boolean enableTrim = RgsEnum.hasOptionTrim(rgsFile);
148: GuardDB db = new GuardDB(inFile, enableTrim);
149: try {
150: InputStream rgsInputStream = (rgsFile.exists() ? new FileInputStream(
151: rgsFile)
152: : null);
153: db.retain(new RgsEnum(rgsInputStream), log);
154: db.logWarnings(log);
155: if (rgsInputStream != null) {
156: rgsInputStream.close();
157: }
158: // If requested in script, trim unused methods, fields, classes
159: if (enableTrim) {
160: db.trim(log);
161: }
162: db.remapTo(outFile, log);
163: } finally {
164: db.close();
165: }
166: } catch (Exception e) {
167: // Log exceptions before exiting
168: log.println();
169: log.println(LOG_ERROR);
170: if (e instanceof java.util.zip.ZipException) {
171: log.println(LOG_ZIP_ERROR);
172: }
173: log.println("# " + e.toString());
174: e.printStackTrace(log);
175: log.println();
176: System.err.println(SEE_LOG_FILE);
177: throw e;
178: } finally {
179: log.flush();
180: log.close();
181: }
182: }
183:
184: // Write a header out to the log file
185: private void writeLogHeader(PrintWriter log) {
186: log
187: .println("# If this log is to be used for incremental obfuscation / patch generation, ");
188: log
189: .println("# add any '.class', '.method', '.field' and '.attribute' restrictions here:");
190: log.println();
191: log.println();
192: log
193: .println("#-DO-NOT-EDIT-BELOW-THIS-LINE------------------DO-NOT-EDIT-BELOW-THIS-LINE--");
194: log.println("#");
195: log.println(LOG_TITLE_PRE_VERSION + Version.getVersion()
196: + LOG_TITLE_POST_VERSION);
197: log.println("#");
198: log.println(LOG_CREATED + new Date().toString());
199: log.println("#");
200: log.println(LOG_INPUT_FILE + inFile.getName());
201: log.println(LOG_OUTPUT_FILE + outFile.getName());
202: log
203: .println(LOG_SCRIPT_FILE
204: + (rgsFile.exists() ? rgsFile.getName()
205: : LOG_NO_SCRIPT));
206: log.println("#");
207: }
208: }
|