/* Copyright (c) 2006, Sun Microsystems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the Sun Microsystems, Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ import java.io.*; import java.util.*; public class Obfuscator extends Globals { // These data structures implement a stack that is used to recursively // walk the input directory structure looking for Java files. static String[][] dirStack = new String[100][]; static int[] dirStackIndex = new int[100]; static int dirStackSize; static File[] dirFile = new File[100]; static { dirFile[0] = inpDir; dirStackSize = 1; dirStack[dirStackSize] = dirFile[dirStackSize-1].list(); dirStackIndex[dirStackSize] = 0; } // Returns true if this is a Java file. static boolean javaFile(String name) { if (name.length() < 6) { return false; } if (name.substring(name.length() - 5).equals(".java")) { return true; } return false; } // The iterator. This uses the above datastructures to walk the input // directory tree. Every time it finds a Java file or when it cannot find // any more Java file, it returns to the caller. static void nextJavaFile() { while (true) { if (dirStackIndex[dirStackSize] == dirStack[dirStackSize].length) { dirStackSize--; if (dirStackSize == 0) { return; } else { dirStackIndex[dirStackSize]++; } } else { dirFile[dirStackSize] = new File(dirFile[dirStackSize-1], dirStack[dirStackSize][dirStackIndex[dirStackSize]]); if (dirStack[dirStackSize][dirStackIndex[dirStackSize]].equals("SCCS")) { dirStackIndex[dirStackSize]++; } else if (dirFile[dirStackSize].isDirectory()) { dirStackSize++; dirStack[dirStackSize] = dirFile[dirStackSize-1].list(); dirStackIndex[dirStackSize] = 0; } else if (javaFile(dirStack[dirStackSize][dirStackIndex[dirStackSize]])) { dirStackIndex[dirStackSize]++; return; } else { dirStackIndex[dirStackSize]++; } } } } // The main Obfuscator routine. It calls the iterator for each Java file to // work on. It then creates the output file, then parses the input Java file // to determine whether or not it has a main program and also to collect the // tokens that make up this file. It then calls printOutputFile that takes // first token and walks the next field chain printing tokens as it goes along. // Finally a main program is created if necessary. static public void start() { boolean parserInitialized = false; JavaParser parser = null; Token first; while (true) { nextJavaFile(); if (dirStackSize == 0) { break; } createOutputFile(); System.out.println("Obfuscating " + dirFile[dirStackSize].getPath()); System.out.println(" into " + outFile.getPath()); try { if (parserInitialized) { parser.ReInit(new FileInputStream(dirFile[dirStackSize])); } else { parser = new JavaParser(new FileInputStream(dirFile[dirStackSize])); parserInitialized = true; } first = parser.CompilationUnit(dirStack[dirStackSize][dirStackIndex[dirStackSize]-1]); } catch (ParseException e1) { System.out.println("Parse error in file " + dirFile[dirStackSize].getPath()); throw new Error(); } catch (IOException e2) { System.out.println("Could not open file " + dirFile[dirStackSize].getPath()); throw new Error(); } printOutputFile(first); if (mainExists) { createMainClass(); } } } static File outFile; static PrintWriter ostr; static void createOutputFile() { // sets outFile and ostr outFile = outDir; for (int i = 1; i < dirStackSize; i++) { outFile = new File(outFile, map(dirStack[i][dirStackIndex[i]])); if (outFile.exists()) { if (!outFile.isDirectory()) { System.out.println("Unexpected error!"); throw new Error(); } } else { if (!outFile.mkdir()) { System.out.println("Could not create directory " + outFile.getPath()); throw new Error(); } } } String origFileName = dirStack[dirStackSize][dirStackIndex[dirStackSize]-1]; String newFileName = map(origFileName.substring(0, origFileName.length() - 5)) + ".java"; outFile = new File(outFile, newFileName); try { ostr = new PrintWriter(new FileWriter(outFile)); } catch (IOException e) { System.out.println("Could not create file " + outFile.getPath()); throw new Error(); } } static void printOutputFile(Token first) { Token t = first; for (int i = 1; i < t.beginColumn; i++) { ostr.print(" "); } while (true) { if (t.kind == JavaParserConstants.IDENTIFIER) { t.image = map(t.image); } ostr.print(addUnicodeEscapes(t.image)); if (t.next == null) { ostr.println(""); break; } if (t.endLine != t.next.beginLine) { for (int i = t.endLine; i < t.next.beginLine; i++) { ostr.println(""); } for (int i = 1; i < t.next.beginColumn; i++) { ostr.print(" "); } } else { for (int i = t.endColumn+1; i < t.next.beginColumn; i++) { ostr.print(" "); } } t = t.next; } ostr.close(); } static String addUnicodeEscapes(String str) { String retval = ""; char ch; for (int i = 0; i < str.length(); i++) { ch = str.charAt(i); if (ch < 0x20 || ch > 0x7e) { String s = "0000" + Integer.toString(ch, 16); retval += "\\u" + s.substring(s.length() - 4, s.length()); } else { retval += ch; } } return retval; } // This creates a main program if there was one in the original file. This // main program has the same name and resides in the same package as the original // file and it simply calls the obfuscated main program. This allows scripts // to continue to work. static void createMainClass() { PrintWriter mstr; boolean mustCreate = false; File mFile = outDir; for (int i = 1; i < dirStackSize; i++) { mFile = new File(mFile, dirStack[i][dirStackIndex[i]]); mustCreate = mustCreate || !map(dirStack[i][dirStackIndex[i]]).equals(dirStack[i][dirStackIndex[i]]); if (mFile.exists()) { if (!mFile.isDirectory()) { System.out.println("Error: Created file " + mFile.getPath() + ", but need to create a main program with the same path prefix. Please remove identifiers from the path prefix from your and run again."); throw new Error(); } } } String origFileName = dirStack[dirStackSize][dirStackIndex[dirStackSize]-1]; String newFileName = map(origFileName.substring(0, origFileName.length() - 5)) + ".java"; if (!mustCreate && origFileName.equals(newFileName)) { return; // this main program has not been obfuscated. } if (!mFile.exists() && !mFile.mkdirs()) { System.out.println("Could not create " + mFile.getPath()); throw new Error(); } mFile = new File(mFile, origFileName); try { mstr = new PrintWriter(new FileWriter(mFile)); } catch (IOException e) { System.out.println("Could not create " + mFile.getPath()); throw new Error(); } System.out.print("Generating main program "); String pname = ""; if (dirStackSize > 1) { for (int i = 1; i < dirStackSize; i++) { pname += "." + dirStack[i][dirStackIndex[i]]; } mstr.println("package " + pname.substring(1) + ";"); System.out.print(pname.substring(1) + "."); mstr.println(""); } System.out.println(origFileName.substring(0, origFileName.length() - 5)); mstr.println("public class " + origFileName.substring(0, origFileName.length() - 5) + " {"); mstr.println(""); mstr.println(" public static void main(String[] args) {"); pname = ""; for (int i = 1; i < dirStackSize; i++) { pname += map(dirStack[i][dirStackIndex[i]]) + "."; } mstr.println(" " + pname + map(origFileName.substring(0, origFileName.length() - 5)) + ".main(args);"); mstr.println(" }"); mstr.println(""); mstr.println("}"); mstr.close(); } }