From 2f04c0e0cbd938dd3bafbcadc7ab4e3160dbff11 Mon Sep 17 00:00:00 2001 From: jochen Date: Fri, 16 Jul 1999 18:24:55 +0000 Subject: [PATCH] obfuscator scripting implemented git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@1090 379699f6-c40d-0410-875b-85095c16579e --- jode/configure.in | 6 + jode/jode/Decompiler.java | 2 + jode/jode/Makefile.am | 10 +- jode/jode/jode.jodescript | 26 ++ jode/jode/obfuscator/ClassBundle.java.in | 235 ++++++++++-- jode/jode/obfuscator/ClassIdentifier.java.in | 17 - jode/jode/obfuscator/ConstantAnalyzer.java.in | 14 +- jode/jode/obfuscator/Identifier.java.in | 5 +- jode/jode/obfuscator/IdentifierMatcher.java | 19 + jode/jode/obfuscator/Main.java.in | 349 ++++++------------ jode/jode/obfuscator/Makefile.am | 1 + jode/jode/obfuscator/MethodIdentifier.java.in | 28 +- jode/jode/obfuscator/ModifierMatcher.java | 8 + .../obfuscator/MultiIdentifierMatcher.java.in | 109 ++++++ jode/jode/obfuscator/NameSwapper.java.in | 99 ++--- jode/jode/obfuscator/OptionHandler.java.in | 8 + .../jode/obfuscator/PackageIdentifier.java.in | 284 +++++++------- jode/jode/obfuscator/ParseException.java | 7 + .../{Renamer.java => Renamer.java.in} | 3 +- jode/jode/obfuscator/ScriptParser.java.in | 258 +++++++++++++ jode/jode/obfuscator/StrongRenamer.java | 63 ---- jode/jode/obfuscator/StrongRenamer.java.in | 141 +++++++ jode/jode/obfuscator/UniqueRenamer.java.in | 21 ++ .../{WildCard.java => WildCard.java.in} | 51 ++- 24 files changed, 1169 insertions(+), 595 deletions(-) create mode 100644 jode/jode/jode.jodescript create mode 100644 jode/jode/obfuscator/MultiIdentifierMatcher.java.in create mode 100644 jode/jode/obfuscator/OptionHandler.java.in create mode 100644 jode/jode/obfuscator/ParseException.java rename jode/jode/obfuscator/{Renamer.java => Renamer.java.in} (92%) create mode 100644 jode/jode/obfuscator/ScriptParser.java.in delete mode 100644 jode/jode/obfuscator/StrongRenamer.java create mode 100644 jode/jode/obfuscator/StrongRenamer.java.in create mode 100644 jode/jode/obfuscator/UniqueRenamer.java.in rename jode/jode/obfuscator/{WildCard.java => WildCard.java.in} (61%) diff --git a/jode/configure.in b/jode/configure.in index a3bc1cc..a7f10eb 100644 --- a/jode/configure.in +++ b/jode/configure.in @@ -161,6 +161,10 @@ jode/flow/IfThenElseBlock.java jode/flow/InstructionBlock.java jode/flow/FlowBlock.java jode/flow/TransformExceptionHandlers.java +jode/obfuscator/Renamer.java +jode/obfuscator/StrongRenamer.java +jode/obfuscator/OptionHandler.java +jode/obfuscator/ScriptParser.java jode/obfuscator/TranslationTable.java jode/obfuscator/Main.java jode/obfuscator/FieldIdentifier.java @@ -172,6 +176,8 @@ jode/obfuscator/ConstantAnalyzer.java jode/obfuscator/LocalIdentifier.java jode/obfuscator/MethodIdentifier.java jode/obfuscator/ClassIdentifier.java +jode/obfuscator/MultiIdentifierMatcher.java +jode/obfuscator/WildCard.java jode/swingui/Main.java jode/swingui/PackagesTreeModel.java jode/util/SimpleSet.java diff --git a/jode/jode/Decompiler.java b/jode/jode/Decompiler.java index 3614f4a..1b4f5d6 100644 --- a/jode/jode/Decompiler.java +++ b/jode/jode/Decompiler.java @@ -100,6 +100,8 @@ public class Decompiler { err.println("Usage: java jode.Decompiler [OPTIONS]... [CLASSES]..."); err.println(" -h, --help "+ "show this information."); + err.println(" -V, --version "+ + "output version information and exit."); err.println(" -v, --verbose "+ "be verbose (multiple times means more verbose)."); err.println(" -c, --classpath "+ diff --git a/jode/jode/Makefile.am b/jode/jode/Makefile.am index 1da5dac..fc0f741 100644 --- a/jode/jode/Makefile.am +++ b/jode/jode/Makefile.am @@ -37,11 +37,5 @@ clean-local: $(JARFILE): $(noinst_DATA) CLASSPATH=$(top_builddir):$(CLASSPATH) $(JAVA) -mx80m \ - jode.obfuscator.Main --dest $(JARFILE) \ - --revtable rename.table \ - --rename=none --breakserial --strip=unreach -v -v \ - --preserve 'jode.Decompiler.main.*' \ - --preserve 'jode.JodeApplet..*' \ - --preserve 'jode.JodeWindow.main.*' \ - --preserve 'jode.obfuscator.Main.main.*' \ - --preserve 'jode.swingui.Main.main.*' jode + jode.obfuscator.Main --classpath=$(top_builddir) \ + --dest=$(JARFILE) -v -v $(srcdir)/jode.jodescript diff --git a/jode/jode/jode.jodescript b/jode/jode/jode.jodescript new file mode 100644 index 0000000..ca576ac --- /dev/null +++ b/jode/jode/jode.jodescript @@ -0,0 +1,26 @@ +# This is a sample script file to obfuscate the JODE project. + +# First we select what we want to strip. There are several possibilities: +# unreach - strip unreachable methods and classes. +# source - strip source file attribute. +# lnt - strip line number table. +# lvt - strip local variable table. +# inner - strip inner class info +strip = "unreach" + +load = new WildCard { value = "jode" } + +preserve = new WildCard { value = "jode.Decompiler.main.*" }, + new WildCard { value = "jode.JodeApplet..()V" }, + new WildCard { value = "jode.JodeWindow.main.*" }, + new WildCard { value = "jode.obfuscator.Main.main.*" }, + new WildCard { value = "jode.swingui.Main.main.*" } + +# value = "jode.Decompiler.main.*", +# "jode.JodeApplet..()V", +# "jode.JodeWindow.main.*", +# "jode.obfuscator.Main.main.*", +# "jode.swingui.Main.main.*" + +analyzer = new SimpleAnalyzer +post = new LocalOptimizer, new RemovePopAnalyzer diff --git a/jode/jode/obfuscator/ClassBundle.java.in b/jode/jode/obfuscator/ClassBundle.java.in index 8442bab..e2e0b28 100644 --- a/jode/jode/obfuscator/ClassBundle.java.in +++ b/jode/jode/obfuscator/ClassBundle.java.in @@ -19,6 +19,7 @@ package jode.obfuscator; import jode.GlobalOptions; +import jode.bytecode.SearchPath; import jode.bytecode.ClassInfo; import jode.bytecode.Reference; import java.io.*; @@ -30,6 +31,7 @@ import @COLLECTIONS@.Set; import @COLLECTIONS@.HashSet; import @COLLECTIONS@.Map; import @COLLECTIONS@.TreeMap; +import @COLLECTIONEXTRA@.UnsupportedOperationException; ///#ifdef JDK12 ///import @COLLECTIONS@.WeakHashMap; @@ -37,17 +39,34 @@ import @COLLECTIONS@.TreeMap; import @COLLECTIONS@.HashMap; ///#endif -public class ClassBundle { - - ModifierMatcher preserveRule = null; +public class ClassBundle implements OptionHandler { PackageIdentifier basePackage; + /** * the identifiers that must be analyzed. */ Set toAnalyze = new HashSet(); + String classPath; + String destDir; + + String tableFile; + String toTableFile; + + IdentifierMatcher loading; + IdentifierMatcher preserving; + IdentifierMatcher reaching; + CodeTransformer[] preTrafos; + CodeAnalyzer analyzer; + CodeTransformer[] postTrafos; + Renamer renamer; + + public ClassBundle() { - basePackage = new PackageIdentifier(this, null, "", false); + classPath = System.getProperty("java.class.path") + .replace(File.pathSeparatorChar, SearchPath.pathSeparatorChar); + destDir = "."; + basePackage = new PackageIdentifier(this, null, ""); basePackage.setReachable(); basePackage.setPreserved(); } @@ -58,6 +77,102 @@ public class ClassBundle { private static final Map aliasesHash = new HashMap(); ///#endif + public static void setStripOptions(Collection stripString) { + } + public void setOption(String option, Collection values) { + if (option.equals("classpath")) { + Iterator i = values.iterator(); + StringBuffer sb = new StringBuffer((String) i.next()); + while (i.hasNext()) { + sb.append(SearchPath.pathSeparatorChar) + .append((String)i.next()); + } + ClassInfo.setClassPath(sb.toString()); + return; + } + + if (option.equals("dest")) { + if (values.size() != 1) + throw new IllegalArgumentException + ("Only one destination path allowed"); + destDir = (String) values.iterator().next(); + return; + } + + if (option.equals("strip")) { + next_token: + for (Iterator iter = values.iterator(); iter.hasNext(); ) { + String token = (String) iter.next(); + for (int i=0; i < Main.stripNames.length; i++) { + if (token.equals(Main.stripNames[i])) { + Main.stripping |= 1 << i; + continue next_token; + } + } + throw new IllegalArgumentException("Unknown strip option: `" + +token+"'"); + } + return; + } + + if (option.equals("load")) { + if (values.size() == 1) + loading = (IdentifierMatcher) values.iterator().next(); + else + loading = new MultiIdentifierMatcher + (MultiIdentifierMatcher.OR, (IdentifierMatcher[]) + values.toArray(new IdentifierMatcher[values.size()])); + return; + } + + if (option.equals("preserve")) { + if (values.size() == 1) + preserving = (IdentifierMatcher) values.iterator().next(); + else + preserving = new MultiIdentifierMatcher + (MultiIdentifierMatcher.OR, (IdentifierMatcher[]) + values.toArray(new IdentifierMatcher[values.size()])); + return; + } + + if (option.equals("reach")) { + // NOT IMPLEMENTED YET + if (values.size() == 1) + reaching = (IdentifierMatcher) values.iterator().next(); + else + reaching = new MultiIdentifierMatcher + (MultiIdentifierMatcher.OR, (IdentifierMatcher[]) + values.toArray(new IdentifierMatcher[values.size()])); + } + + if (option.equals("pre")) { + preTrafos = (CodeTransformer[]) + values.toArray(new CodeTransformer[values.size()]); + return; + } + if (option.equals("analyzer")) { + if (values.size() != 1) + throw new IllegalArgumentException + ("Only one analyzer is allowed"); + analyzer = (CodeAnalyzer) values.iterator().next(); + return; + } + if (option.equals("post")) { + postTrafos = (CodeTransformer[]) + values.toArray(new CodeTransformer[values.size()]); + return; + } + + if (option.equals("renamer")) { + if (values.size() != 1) + throw new IllegalArgumentException + ("Only one renamer allowed"); + renamer = (Renamer) values.iterator().next(); + return; + } + throw new IllegalArgumentException("Invalid option `"+option+"'."); + } + public Reference getReferenceAlias(Reference ref) { Reference alias = (Reference) aliasesHash.get(ref); if (alias == null) { @@ -113,27 +228,10 @@ public class ClassBundle { return ident.getIdentifier(ref.getName(), ref.getType()); } - public void loadClasses(String wildcard) { - System.err.println("Loading: "+wildcard); - basePackage.loadMatchingClasses(new WildCard(wildcard)); - } - public void reachableIdentifier(String fqn, boolean isVirtual) { basePackage.reachableIdentifier(fqn, isVirtual); } - public void setPreserved(ModifierMatcher preserveRule, - Collection fullqualifiednames) { - this.preserveRule = preserveRule; - - basePackage.applyPreserveRule(preserveRule); - for (Iterator i = fullqualifiednames.iterator(); i.hasNext(); ) { - basePackage.preserveMatchingIdentifier - (new WildCard((String) i.next())); - } - analyze(); - } - public void analyzeIdentifier(Identifier ident) { if (ident == null) throw new NullPointerException(); @@ -147,33 +245,49 @@ public class ClassBundle { ident.analyze(); } } + + public IdentifierMatcher getPreserveRule() { + return preserving; + } + + public CodeAnalyzer getCodeAnalyzer() { + return analyzer; + } + + public CodeTransformer[] getPreTransformers() { + return preTrafos; + } + + public CodeTransformer[] getPostTransformers() { + return postTrafos; + } public void buildTable(Renamer renameRule) { basePackage.buildTable(renameRule); } - public void readTable(String filename) { + public void readTable() { try { TranslationTable table = new TranslationTable(); - InputStream input = new FileInputStream(filename); + InputStream input = new FileInputStream(tableFile); table.load(input); input.close(); basePackage.readTable(table); } catch (java.io.IOException ex) { - GlobalOptions.err.println("Can't read rename table "+filename); + GlobalOptions.err.println("Can't read rename table " + tableFile); ex.printStackTrace(GlobalOptions.err); } } - public void writeTable(String filename) { + public void writeTable() { TranslationTable table = new TranslationTable(); basePackage.writeTable(table); try { - OutputStream out = new FileOutputStream(filename); + OutputStream out = new FileOutputStream(toTableFile); table.store(out); out.close(); } catch (java.io.IOException ex) { - GlobalOptions.err.println("Can't write rename table "+filename); + GlobalOptions.err.println("Can't write rename table "+toTableFile); ex.printStackTrace(GlobalOptions.err); } } @@ -182,28 +296,77 @@ public class ClassBundle { basePackage.doTransformations(); } - public void storeClasses(String destination) { - if (destination.endsWith(".jar") || - destination.endsWith(".zip")) { + public void storeClasses() { + if (destDir.endsWith(".jar") || + destDir.endsWith(".zip")) { try { ZipOutputStream zip = new ZipOutputStream - (new FileOutputStream(destination)); + (new FileOutputStream(destDir)); basePackage.storeClasses(zip); zip.close(); } catch (IOException ex) { GlobalOptions.err.println - ("Can't write zip file: "+destination); + ("Can't write zip file: "+destDir); ex.printStackTrace(GlobalOptions.err); } } else { - File directory = new File(destination); + File directory = new File(destDir); if (!directory.exists()) { GlobalOptions.err.println("Destination directory " - +directory.getPath() - +" doesn't exists."); + +directory.getPath() + +" doesn't exists."); return; } - basePackage.storeClasses(new File(destination)); + basePackage.storeClasses(new File(destDir)); } } + + public void run() { + if (analyzer == null) + analyzer = new SimpleAnalyzer(); + if (preTrafos == null) + preTrafos = new CodeTransformer[0]; + if (postTrafos == null) + postTrafos = new CodeTransformer[0]; + if (renamer == null) + renamer = new Renamer() { + public Iterator generateNames(Identifier ident) { + final String base = ident.getName(); + return new Iterator() { + int last = 0; + + public boolean hasNext() { + return true; + } + + public Object next() { + return (last++ == 1 ? base : base + last); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + }; + + GlobalOptions.err.println("Loading and preserving classes"); + basePackage.loadMatchingClasses(loading); + basePackage.applyPreserveRule(preserving); + + GlobalOptions.err.println("Computing reachable settings"); + analyze(); + + GlobalOptions.err.println("Renaming methods"); + if (tableFile != null) + readTable(); + buildTable(renamer); + if (toTableFile != null) + writeTable(); + + GlobalOptions.err.println("Transforming the classes"); + doTransformations(); + GlobalOptions.err.println("Writing new classes"); + storeClasses(); + } } diff --git a/jode/jode/obfuscator/ClassIdentifier.java.in b/jode/jode/obfuscator/ClassIdentifier.java.in index 0aa72c1..5423049 100644 --- a/jode/jode/obfuscator/ClassIdentifier.java.in +++ b/jode/jode/obfuscator/ClassIdentifier.java.in @@ -66,23 +66,6 @@ public class ClassIdentifier extends Identifier { } } - public void preserveMatchingIdentifier(WildCard wildcard) { - String fullName = getFullName() + "."; - for (Iterator i = getChilds(); i.hasNext(); ) { - Identifier ident = (Identifier) i.next(); - System.err.println("checking "+ident); - if (wildcard.matches(fullName + ident.getName()) - || wildcard.matches(fullName + ident.getName() - + "." +ident.getType())) { - if (GlobalOptions.verboseLevel > 0) - GlobalOptions.err.println("Preserving "+ident); - setPreserved(); - ident.setPreserved(); - ident.setReachable(); - } - } - } - private FieldIdentifier findField(String name, String typeSig) { for (Iterator i = fieldIdents.iterator(); i.hasNext(); ) { FieldIdentifier ident = (FieldIdentifier) i.next(); diff --git a/jode/jode/obfuscator/ConstantAnalyzer.java.in b/jode/jode/obfuscator/ConstantAnalyzer.java.in index c25be7a..3b7f55d 100644 --- a/jode/jode/obfuscator/ConstantAnalyzer.java.in +++ b/jode/jode/obfuscator/ConstantAnalyzer.java.in @@ -46,11 +46,9 @@ import @COLLECTIONS@.Iterator; * @author Jochen Hoenicke */ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { - boolean working; Map constInfos; BytecodeInfo bytecode; - Identifier listener; private static ConstantRuntimeEnvironment runtime = new ConstantRuntimeEnvironment(); @@ -485,7 +483,7 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { } } - public void handleOpcode(StackLocalInfo info) { + public void handleOpcode(StackLocalInfo info, Identifier fieldListener) { Instruction instr = info.instr; constInfos.put(instr, unknownConstInfo); @@ -1206,7 +1204,7 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { shortInfo.constant = obj; result = new ConstValue(obj); result.addConstantListener(shortInfo); - fi.addFieldListener(listener); + fi.addFieldListener(fieldListener); } } else result = unknownValue[type.stackSize()-1]; @@ -1367,10 +1365,9 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { } } - public void analyzeCode(MethodIdentifier listener, BytecodeInfo bytecode) { - this.listener = listener; + public void analyzeCode(MethodIdentifier methodIdent, + BytecodeInfo bytecode) { this.bytecode = bytecode; - working = true; if (constInfos == null) constInfos = new HashMap(); Set modifiedQueue = new HashSet(); @@ -1385,7 +1382,7 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { Iterator iter = modifiedQueue.iterator(); StackLocalInfo info = (StackLocalInfo) iter.next(); iter.remove(); - handleOpcode(info); + handleOpcode(info, methodIdent); } Handler[] handlers = bytecode.getExceptionHandlers(); @@ -1394,7 +1391,6 @@ public class ConstantAnalyzer implements Opcodes, CodeAnalyzer { && handlers[i].type != null) Main.getClassBundle().reachableIdentifier(handlers[i].type, false); } - working = false; for (Instruction instr = bytecode.getFirstInstr(); instr != null; instr = instr.getNextByAddr()) instr.setTmpInfo(null); diff --git a/jode/jode/obfuscator/Identifier.java.in b/jode/jode/obfuscator/Identifier.java.in index fc9f6d7..e9ac883 100644 --- a/jode/jode/obfuscator/Identifier.java.in +++ b/jode/jode/obfuscator/Identifier.java.in @@ -181,10 +181,10 @@ public abstract class Identifier { // set alias to empty string, so it won't conflict! rep.alias = ""; - String newAlias = null; + Iterator aliases = renameRule.generateNames(this); next_alias: for (;;) { - newAlias = renameRule.generateName(this, newAlias); + String newAlias = (String) aliases.next(); Identifier ptr = rep; while (ptr != null) { if (ptr.conflicting(newAlias)) @@ -224,6 +224,7 @@ public abstract class Identifier { public void applyPreserveRule(IdentifierMatcher preserveRule) { if (preserveRule.matches(this)) { + System.err.println("preserving: "+this); setReachable(); setPreserved(); } diff --git a/jode/jode/obfuscator/IdentifierMatcher.java b/jode/jode/obfuscator/IdentifierMatcher.java index 1fbf56e..ff3bdb9 100644 --- a/jode/jode/obfuscator/IdentifierMatcher.java +++ b/jode/jode/obfuscator/IdentifierMatcher.java @@ -20,6 +20,25 @@ package jode.obfuscator; public interface IdentifierMatcher { + /** + * Returns true, if the ident is matched by this matcher. + */ public boolean matches(Identifier ident); + + /** + * Returns true, if there may be a sub ident, that is matched by + * this matcher. + * @param subIdent the name of the sub ident, or null if every + * name is okay. + */ + public boolean matchesSub(Identifier ident, String subIdent); + + /** + * Returns the unique name of the single sub item, for which matches + * or matchesSub returns true. + * @return the unique name, or null, if there is not a unique sub + * item. + */ + public String getNextComponent(Identifier ident); } diff --git a/jode/jode/obfuscator/Main.java.in b/jode/jode/obfuscator/Main.java.in index 34675d4..457b996 100644 --- a/jode/jode/obfuscator/Main.java.in +++ b/jode/jode/obfuscator/Main.java.in @@ -21,14 +21,15 @@ package jode.obfuscator; import jode.bytecode.ClassInfo; import jode.bytecode.SearchPath; import jode.GlobalOptions; -import java.util.Vector; -import java.util.StringTokenizer; + +import gnu.getopt.LongOpt; +import gnu.getopt.Getopt; + import java.lang.reflect.Modifier; import java.io.PrintWriter; -import java.io.File; -import @COLLECTIONS@.Collection; -import @COLLECTIONS@.Arrays; -import @COLLECTIONS@.HashSet; +import java.io.FileReader; +import java.io.IOException; +import @COLLECTIONS@.Collections; public class Main { public static boolean swapOrder = false; @@ -37,6 +38,16 @@ public class Main { public static final int OPTION_PRESERVESERIAL = 0x0002; public static int options = OPTION_PRESERVESERIAL; + private static final LongOpt[] longOptions = new LongOpt[] { + new LongOpt("cp", LongOpt.REQUIRED_ARGUMENT, null, 'c'), + new LongOpt("classpath", LongOpt.REQUIRED_ARGUMENT, null, 'c'), + new LongOpt("destpath", LongOpt.REQUIRED_ARGUMENT, null, 'd'), + new LongOpt("help", LongOpt.NO_ARGUMENT, null, 'h'), + new LongOpt("version", LongOpt.NO_ARGUMENT, null, 'V'), + new LongOpt("verbose", LongOpt.OPTIONAL_ARGUMENT, null, 'v'), + new LongOpt("debug", LongOpt.OPTIONAL_ARGUMENT, null, 'D'), + }; + public static final String[] stripNames = { "unreach", "inner", "lvt", "lnt", "source" }; @@ -51,252 +62,118 @@ public class Main { public static void usage() { PrintWriter err = GlobalOptions.err; - err.println("usage: jode.Obfuscator flags* [class | package]*"); - err.println("\t-v "+ - "Verbose output (allowed multiple times)."); - err.println("\t--nostrip "+ - "Don't strip not needed methods"); - - err.println("\t--cp "+ - "The class path; should contain classes.zip"); - err.println("\t-d,--dest "+ - "Destination directory for output classes"); - err.println("Preserve options: "); - err.println("\t--package "+ - "Preserve all package members"); - err.println("\t--protected "+ - "Preserve all protected members"); - err.println("\t--public "+ - "Preserve all public members"); - err.println("\t--preserve "+ - "Preserve only the given name (allowed multiple times)"); - err.println("\t--breakserial "+ - "Allow the serialized form to change"); - err.println("Obfuscating options: "); - err.println("\t--rename={strong|weak|unique|none} "+ - "Rename identifiers with given scheme"); - err.println("\t--strip=... "+ - "use --strip=help for more information."); - err.println("\t--table "+ - "Read (some) translation table from file"); - err.println("\t--revtable "+ - "Write reversed translation table to file"); - err.println("\t--swaporder "+ - "Swap the order of fields and methods."); - err.println("\t--debug=... "+ + err.println("usage: jode.Obfuscator flags* script"); + err.println(" -h, --help "+ + "show this information."); + err.println(" -V, --version "+ + "output version information and exit."); + err.println(" -v, --verbose "+ + "be verbose (multiple times means more verbose)."); + err.println(" -c, --classpath "+ + "search for classes in specified classpath."); + err.println(" "+ + "The directories should be separated by ','."); + err.println(" -d, --dest "+ + "write decompiled files to disk into directory destdir."); + err.println(" -D, --debug=... "+ "use --debug=help for more information."); } - public static void usageStrip() { - PrintWriter err = GlobalOptions.err; - err.println("Strip options: --strip=flag1,flag2,..."); - err.println("possible flags:"); - err.println("\tunreach " + - "strip all unreachable methods and classes."); - err.println("\tinner " + - "strip inner classes info."); - err.println("\tlvt " + - "strip local variable tables."); - err.println("\tlnt " + - "strip line number tables."); - err.println("\tsource " + - "strip the name of the source file."); - err.println("\tinout " + - "show T1/T2 in/out set analysis."); - err.println("\tlvt " + - "dump LocalVariableTable."); - err.println("\tcheck " + - "do time consuming sanity checks."); - err.println("\tlocals " + - "dump local merging information."); - err.println("\tconstructors " + - "dump constructor simplification."); - err.println("\tinterpreter " + - "debug execution of interpreter."); - System.exit(0); - } - - public static void setStripOptions(String stripString) { - if (stripString.length() == 0 || stripString.equals("help")) - usageStrip(); - - StringTokenizer st = new StringTokenizer(stripString, ","); - next_token: - while (st.hasMoreTokens()) { - String token = st.nextToken().intern(); - for (int i=0; i < stripNames.length; i++) { - if (token == stripNames[i]) { - stripping |= 1 << i; - continue next_token; - } - } - GlobalOptions.err.println("Illegal strip flag: "+token); - usageStrip(); - } - } - - public static Renamer getRenamer(String option) { - if (option.equals("strong")) - return new StrongRenamer - ("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", - "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); - else if (option.equals("weak")) - return new StrongRenamer - ("abcdefghijklmnopqrstuvwxyz", - "0123456789abcdefghijklmnopqrstuvwxyz"); - else if (option.equals("unique")) { - return new Renamer() { - static int serialnr = 0; - public String generateName(Identifier ident, String lastName) { - return ("xxx" + serialnr++); - } - }; - } else if (option.equals("none")) { - return new Renamer() { - public String generateName(Identifier ident, String lastName) { - String name = ident.getName(); - if (lastName == null) - return name; - - int nr = 2; - if (lastName.length() > name.length()) - nr = 1 + Integer.parseInt - (lastName.substring(name.length())); - return name + nr; - } - }; - } - GlobalOptions.err.println("Incorrect value for --rename option: " - + option); - usage(); - System.exit(0); - return null; - } public static ClassBundle getClassBundle() { return bundle; } - public static CodeAnalyzer createCodeAnalyzer() { - return new SimpleAnalyzer() /*XXX*/; - } - - static CodeTransformer[] codeTransformers = { - new LocalOptimizer(), - new RemovePopAnalyzer() - }; - - public static Collection getCodeTransformers() { - return Arrays.asList(codeTransformers); - } - public static void main(String[] params) { - int i; - String sourcePath = System.getProperty("java.class.path") - .replace(File.pathSeparatorChar, SearchPath.pathSeparatorChar); - String destPath = "."; - - Collection preservedIdents = new HashSet(); - - ModifierMatcher preserveRule = ModifierMatcher.denyAll; - Renamer renamer = null; - String table = null; - String toTable = null; - - for (i=0; i 5) + ex.printStackTrace(GlobalOptions.err); + return; + } - GlobalOptions.err.println("Renaming methods"); - if (table != null) - bundle.readTable(table); - bundle.buildTable(renamer); - if (toTable != null) - bundle.writeTable(toTable); + // Command Line overwrites script options: + if (cp != null) + bundle.setOption("classpath", Collections.singleton(cp)); + if (dest != null) + bundle.setOption("dest", Collections.singleton(dest)); - GlobalOptions.err.println("Transforming the classes"); - bundle.doTransformations(); - GlobalOptions.err.println("Writing new classes"); - bundle.storeClasses(destPath); + bundle.run(); } } + diff --git a/jode/jode/obfuscator/Makefile.am b/jode/jode/obfuscator/Makefile.am index e27634b..0c9adfd 100644 --- a/jode/jode/obfuscator/Makefile.am +++ b/jode/jode/obfuscator/Makefile.am @@ -23,6 +23,7 @@ MY_JAVA_FILES = \ Main.java \ MethodIdentifier.java \ ModifierMatcher.java \ + MultiIdentifierMatcher.java \ NameSwapper.java \ OptionHandler.java \ PackageIdentifier.java \ diff --git a/jode/jode/obfuscator/MethodIdentifier.java.in b/jode/jode/obfuscator/MethodIdentifier.java.in index 76aa2e5..d4ce75a 100644 --- a/jode/jode/obfuscator/MethodIdentifier.java.in +++ b/jode/jode/obfuscator/MethodIdentifier.java.in @@ -60,9 +60,15 @@ public class MethodIdentifier extends Identifier implements Opcodes { info.getBytecode().setLocalVariableTable(null); if ((Main.stripping & Main.STRIP_LNT) != 0) info.getBytecode().setLineNumberTable(null); - codeAnalyzer = Main.createCodeAnalyzer(); - } + codeAnalyzer = Main.getClassBundle().getCodeAnalyzer(); + CodeTransformer[] trafos + = Main.getClassBundle().getPreTransformers(); + for (int i = 0; i < trafos.length; i++) { + trafos[i].transformCode(bytecode); + } + info.setBytecode(bytecode); + } } public Iterator getChilds() { @@ -105,8 +111,7 @@ public class MethodIdentifier extends Identifier implements Opcodes { } public void writeTable(Map table) { - table.put(getFullAlias() + "." - + Main.getClassBundle().getTypeAlias(getType()), getName()); + table.put(getFullAlias(), getName()); } public Identifier getParent() { @@ -114,11 +119,12 @@ public class MethodIdentifier extends Identifier implements Opcodes { } public String getFullName() { - return clazz.getFullName() + "." + getName(); + return clazz.getFullName() + "." + getName() + "." + getType(); } public String getFullAlias() { - return clazz.getFullAlias() + "." + getAlias(); + return clazz.getFullAlias() + "." + getAlias() + "." + + Main.getClassBundle().getTypeAlias(getType()); } public String getName() { @@ -167,15 +173,15 @@ public class MethodIdentifier extends Identifier implements Opcodes { ("doTransformation called on transformed method"); wasTransformed = true; info.setName(getAlias()); - info.setType(Main.getClassBundle().getTypeAlias(type)); + ClassBundle bundle = Main.getClassBundle(); + info.setType(bundle.getTypeAlias(type)); if (codeAnalyzer != null) { BytecodeInfo bytecode = info.getBytecode(); try { codeAnalyzer.transformCode(bytecode); - for (Iterator i = Main.getCodeTransformers().iterator(); - i.hasNext(); ) { - CodeTransformer transformer = (CodeTransformer) i.next(); - transformer.transformCode(bytecode); + CodeTransformer[] trafos = bundle.getPostTransformers(); + for (int i = 0; i < trafos.length; i++) { + trafos[i].transformCode(bytecode); } } catch (RuntimeException ex) { ex.printStackTrace(GlobalOptions.err); diff --git a/jode/jode/obfuscator/ModifierMatcher.java b/jode/jode/obfuscator/ModifierMatcher.java index 806b863..a848596 100644 --- a/jode/jode/obfuscator/ModifierMatcher.java +++ b/jode/jode/obfuscator/ModifierMatcher.java @@ -251,6 +251,14 @@ public class ModifierMatcher implements IdentifierMatcher, Cloneable { return matches(modifiers); } + public final boolean matchesSub(Identifier ident, String name) { + return true; + } + + public final String getNextComponent(Identifier ident) { + return null; + } + public Object clone() { try { return super.clone(); diff --git a/jode/jode/obfuscator/MultiIdentifierMatcher.java.in b/jode/jode/obfuscator/MultiIdentifierMatcher.java.in new file mode 100644 index 0000000..aa700e3 --- /dev/null +++ b/jode/jode/obfuscator/MultiIdentifierMatcher.java.in @@ -0,0 +1,109 @@ +/* AndIdentifierMatcher Copyright (C) 1999 Jochen Hoenicke. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id$ + */ + +package jode.obfuscator; +import @COLLECTIONS@.Collection; + +public class MultiIdentifierMatcher implements IdentifierMatcher, OptionHandler { + /** + * Useful constant for giving to the constructor. + */ + public static boolean OR = true; + /** + * Useful constant for giving to the constructor. + */ + public static boolean AND = false; + + IdentifierMatcher[] matchers; + boolean isOr; + + /** + * Create an empty MultiIdentifierMatcher. + */ + public MultiIdentifierMatcher() { + this.matchers = new IdentifierMatcher[0]; + } + + /** + * Create an IdentifierMatcher out of other matchers. + * @param isOr if true, match should return the logical (shortcut) + * or of the underlying matchers, if false it returns the logical and. + * @param matchers the underlying matchers + */ + public MultiIdentifierMatcher(boolean isOr, + IdentifierMatcher[] matchers) { + this.isOr = isOr; + this.matchers = matchers; + } + + public void setOption(String option, Collection values) { + if (option.equals("or")) { + isOr = true; + matchers = (IdentifierMatcher[]) + values.toArray(new IdentifierMatcher[values.size()]); + } else if (option.equals("and")) { + isOr = false; + matchers = (IdentifierMatcher[]) + values.toArray(new IdentifierMatcher[values.size()]); + } else + throw new IllegalArgumentException("Invalid option `"+option+"'."); + } + + + public boolean matches(Identifier ident) { + for (int i=0; i< matchers.length; i++) { + if (matchers[i].matches(ident) == isOr) + return isOr; + } + return !isOr; + } + + public boolean matchesSub(Identifier ident, String name) { + for (int i=0; i< matchers.length; i++) { + if (matchers[i].matchesSub(ident, name) == isOr) + return isOr; + } + return !isOr; + } + + public String getNextComponent(Identifier ident) { + if (isOr == AND) { + for (int i=0; i< matchers.length; i++) { + String next = matchers[i].getNextComponent(ident); + if (next != null && matchesSub(ident, next)) + return next; + } + return null; + } + // OR case + String next = null; + for (int i = 0; i < matchers.length; i++) { + if (!matchesSub(ident, null)) + continue; + if (next != null + && matchers[i].getNextComponent(ident) != next) + return null; + next = matchers[i].getNextComponent(ident); + if (next == null) + return null; + } + return next; + } +} + diff --git a/jode/jode/obfuscator/NameSwapper.java.in b/jode/jode/obfuscator/NameSwapper.java.in index a437d12..f9ecbc5 100644 --- a/jode/jode/obfuscator/NameSwapper.java.in +++ b/jode/jode/obfuscator/NameSwapper.java.in @@ -18,12 +18,13 @@ */ package jode.obfuscator; -import java.util.Random; import @COLLECTIONS@.Collection; import @COLLECTIONS@.Set; import @COLLECTIONS@.HashSet; import @COLLECTIONS@.Iterator; +import @COLLECTIONS@.Random; +import @COLLECTIONEXTRA@.UnsupportedOperationException; public class NameSwapper implements Renamer { @@ -46,74 +47,52 @@ public class NameSwapper implements Renamer { this(swapAll, System.currentTimeMillis()); } - private void addName(Collection coll, String name) { - coll.add(name); - } - - private String getName(Collection coll) { -///#ifdef JDK12 -/// int pos = rand.nextInt(coll.size()); -///#else - int pos = rand.nextInt() % coll.size(); -///#endif - Iterator i = coll.iterator(); - while (pos > 0) - i.next(); - return (String) i.next(); - } - - public final void addPackageName(String name) { - addName(packs, name); - } - - public final void addClassName(String name) { - addName(clazzes, name); - } - - public final void addMethodName(String name) { - addName(methods, name); - } + private class NameGenerator implements Iterator { + Collection pool; + + NameGenerator(Collection c) { + pool = c; + } - public final void addFieldName(String name) { - addName(fields, name); - } + public boolean hasNext() { + return true; + } - public final void addLocalName(String name) { - addName(locals, name); - } + public Object next() { + int pos = rand.nextInt(pool.size()); + Iterator i = pool.iterator(); + while (pos > 0) + i.next(); + return (String) i.next(); + } - public final String getPackageName() { - return getName(packs); + public void remove() { + throw new UnsupportedOperationException(); + } } - public final String getClassName() { - return getName(clazzes); + public final Collection getCollection(Identifier ident) { + if (ident instanceof PackageIdentifier) + return packs; + else if (ident instanceof ClassIdentifier) + return clazzes; + else if (ident instanceof MethodIdentifier) + return methods; + else if (ident instanceof FieldIdentifier) + return fields; + else if (ident instanceof LocalIdentifier) + return locals; + else + throw new IllegalArgumentException(ident.getClass().getName()); } - public final String getMethodName() { - return getName(methods); + public final void addIdentifierName(Identifier ident) { + getCollection(ident).add(ident.getName()); } - public final String getFieldName() { - return getName(fields); + public Iterator generateNames(Identifier ident) { + return new NameGenerator(getCollection(ident)); } +} - public final String getLocalName() { - return getName(locals); - } - public String generateName(Identifier ident, String lastName) { - Collection coll = null; - if (ident instanceof PackageIdentifier) - coll = packs; - else if (ident instanceof ClassIdentifier) - coll = clazzes; - else if (ident instanceof MethodIdentifier) - coll = methods; - else if (ident instanceof FieldIdentifier) - coll = fields; -// else if (ident instanceof LocalIdentifier) -// coll = locals; - return getName(coll); - } -} diff --git a/jode/jode/obfuscator/OptionHandler.java.in b/jode/jode/obfuscator/OptionHandler.java.in new file mode 100644 index 0000000..9adb349 --- /dev/null +++ b/jode/jode/obfuscator/OptionHandler.java.in @@ -0,0 +1,8 @@ +package jode.obfuscator; + +import @COLLECTIONS@.Collection; + +public interface OptionHandler { + public void setOption(String option, Collection values) + throws IllegalArgumentException; +} diff --git a/jode/jode/obfuscator/PackageIdentifier.java.in b/jode/jode/obfuscator/PackageIdentifier.java.in index 32b305c..95bf577 100644 --- a/jode/jode/obfuscator/PackageIdentifier.java.in +++ b/jode/jode/obfuscator/PackageIdentifier.java.in @@ -43,14 +43,12 @@ public class PackageIdentifier extends Identifier { public PackageIdentifier(ClassBundle bundle, PackageIdentifier parent, - String name, boolean loadOnDemand) { + String name) { super(name); this.bundle = bundle; this.parent = parent; this.name = name; this.loadedClasses = new HashMap(); - if (loadOnDemand) - setLoadOnDemand(); } /** @@ -94,18 +92,21 @@ public class PackageIdentifier extends Identifier { ? fullname + "."+ subclazz : subclazz; if (ClassInfo.isPackage(fullname)) { - Identifier ident = new PackageIdentifier - (bundle, this, subclazz, true); + PackageIdentifier ident = new PackageIdentifier + (bundle, this, subclazz); loadedClasses.put(subclazz, ident); + ident.setLoadOnDemand(); } else { Identifier ident = new ClassIdentifier (this, subclazz, ClassInfo.forName(fullname)); loadedClasses.put(subclazz, ident); + if (GlobalOptions.verboseLevel > 0) + GlobalOptions.err.println("preloading "+ident); ((ClassIdentifier) ident).initClass(); - if (bundle.preserveRule != null) - ident.applyPreserveRule(bundle.preserveRule); } - } + } + // Everything is loaded, we don't need to load on demand anymore. + loadOnDemand = false; } } @@ -114,7 +115,6 @@ public class PackageIdentifier extends Identifier { Identifier ident = loadClass(name); return ident; } - int index = name.indexOf('.'); if (index == -1) return (Identifier) loadedClasses.get(name); @@ -138,8 +138,11 @@ public class PackageIdentifier extends Identifier { ? fullname + "."+ name : name; if (ClassInfo.isPackage(fullname)) { - ident = new PackageIdentifier(bundle, this, name, true); - loadedClasses.put(name, ident); + PackageIdentifier pack + = new PackageIdentifier(bundle, this, name); + loadedClasses.put(name, pack); + pack.setLoadOnDemand(); + ident = pack; } else if (!ClassInfo.exists(fullname)) { GlobalOptions.err.println("Warning: Can't find class " + fullname); @@ -149,8 +152,6 @@ public class PackageIdentifier extends Identifier { ClassInfo.forName(fullname)); loadedClasses.put(name, ident); ((ClassIdentifier) ident).initClass(); - if (bundle.preserveRule != null) - ident.applyPreserveRule(bundle.preserveRule); } } return ident; @@ -164,8 +165,10 @@ public class PackageIdentifier extends Identifier { ? fullname + "."+ subpack : subpack; if (ClassInfo.isPackage(fullname)) { pack = new PackageIdentifier(bundle, this, - subpack, loadOnDemand); + subpack); loadedClasses.put(subpack, pack); + if (loadOnDemand) + pack.setLoadOnDemand(); } } @@ -176,8 +179,8 @@ public class PackageIdentifier extends Identifier { } } - public void loadMatchingClasses(WildCard wildcard) { - String component = wildcard.getNextComponent(getFullName()); + public void loadMatchingClasses(IdentifierMatcher matcher) { + String component = matcher.getNextComponent(this); if (component != null) { Identifier ident = (Identifier) loadedClasses.get(component); if (ident == null) { @@ -187,33 +190,34 @@ public class PackageIdentifier extends Identifier { : component; if (ClassInfo.isPackage(fullname)) { ident = new PackageIdentifier(bundle, this, - component, loadOnDemand); + component); loadedClasses.put(component, ident); - } else if (ClassInfo.exists(fullname) - && wildcard.matches(fullname)) { + if (loadOnDemand) + ((PackageIdentifier) ident).setLoadOnDemand(); + } else if (ClassInfo.exists(fullname)) { if (GlobalOptions.verboseLevel > 1) GlobalOptions.err.println("loading Class " +fullname); ident = new ClassIdentifier(this, component, ClassInfo.forName(fullname)); - loadedClasses.put(component, ident); - ((ClassIdentifier) ident).initClass(); - if (bundle.preserveRule != null) - ident.applyPreserveRule(bundle.preserveRule); + if (loadOnDemand || matcher.matches(ident)) { + loadedClasses.put(component, ident); + ((ClassIdentifier) ident).initClass(); + } } else { GlobalOptions.err.println ("Warning: Can't find class/package " + fullname); } } if (ident instanceof PackageIdentifier) { - if (wildcard.matches(ident.getFullName())) { + if (matcher.matches(ident)) { if (GlobalOptions.verboseLevel > 0) GlobalOptions.err.println("loading Package " +ident.getFullName()); ((PackageIdentifier) ident).setLoadOnDemand(); } - if (wildcard.startsWith(ident.getFullName()+".")) - ((PackageIdentifier) ident).loadMatchingClasses(wildcard); + if (matcher.matchesSub(ident, null)) + ((PackageIdentifier) ident).loadMatchingClasses(matcher); } } else { String fullname = getFullName(); @@ -228,47 +232,49 @@ public class PackageIdentifier extends Identifier { continue; String subFull = fullname + subclazz; - if (wildcard.matches(subFull)) { + if (matcher.matchesSub(this, subclazz)) { if (ClassInfo.isPackage(subFull)) { if (GlobalOptions.verboseLevel > 0) - GlobalOptions.err.println("loading Package " +subFull); - Identifier ident = new PackageIdentifier - (bundle, this, subclazz, true); + GlobalOptions.err.println("loading Package " + + subFull); + PackageIdentifier ident = new PackageIdentifier + (bundle, this, subclazz); loadedClasses.put(subclazz, ident); + if (loadOnDemand || matcher.matches(ident)) + ident.setLoadOnDemand(); } else { - if (GlobalOptions.verboseLevel > 1) - GlobalOptions.err.println("loading Class " +subFull); ClassIdentifier ident = new ClassIdentifier (this, subclazz, ClassInfo.forName(subFull)); - loadedClasses.put(subclazz, ident); - ((ClassIdentifier) ident).initClass(); - if (bundle.preserveRule != null) - ident.applyPreserveRule(bundle.preserveRule); + + if (loadOnDemand || matcher.matches(ident)) { + if (GlobalOptions.verboseLevel > 1) + GlobalOptions.err.println("loading Class " + + subFull); + loadedClasses.put(subclazz, ident); + ((ClassIdentifier) ident).initClass(); + } } - } else if (ClassInfo.isPackage(subFull) - && wildcard.startsWith(subFull + ".")) { - Identifier ident = new PackageIdentifier - (bundle, this, subclazz, loadOnDemand); - loadedClasses.put(subclazz, ident); } } for (Iterator i = loadedClasses.values().iterator(); i.hasNext(); ) { Identifier ident = (Identifier) i.next(); if (ident instanceof PackageIdentifier) { - if (wildcard.matches(ident.getFullName())) + if (matcher.matches(ident)) ((PackageIdentifier) ident).setLoadOnDemand(); - if (wildcard.startsWith(ident.getFullName()+".")) - ((PackageIdentifier) ident).loadMatchingClasses(wildcard); + if (matcher.matchesSub(ident, null)) + ((PackageIdentifier) ident) + .loadMatchingClasses(matcher); } } } } - public void applyPreserveRule(ModifierMatcher preserveRule) { - for (Iterator i = loadedClasses.values().iterator(); i.hasNext(); ) - ((Identifier) i.next()).applyPreserveRule(preserveRule); + public void applyPreserveRule(IdentifierMatcher preserveRule) { + if (loadOnDemand) + loadMatchingClasses(preserveRule); + super.applyPreserveRule(preserveRule); } public void reachableIdentifier(String fqn, boolean isVirtual) { @@ -300,99 +306,99 @@ public class PackageIdentifier extends Identifier { } } - public void preserveMatchingIdentifier(WildCard wildcard) { - String component = wildcard.getNextComponent(getFullName()); - if (component != null) { - String fullname = getFullName(); - fullname = (fullname.length() > 0) - ? fullname + "."+ component : component; - Identifier ident = (Identifier) loadedClasses.get(component); - if (ident == null) { - if (!loadOnDemand) { - GlobalOptions.err.println - ("Warning: Didn't load package/class "+ fullname); - return; - } - if (ClassInfo.isPackage(fullname)) { - ident = new PackageIdentifier(bundle, this, - component, loadOnDemand); - loadedClasses.put(component, ident); - } else if (ClassInfo.exists(fullname)) { - ident = new ClassIdentifier(this, component, - ClassInfo.forName(fullname)); - loadedClasses.put(component, ident); - ((ClassIdentifier) ident).initClass(); - if (bundle.preserveRule != null) - ident.applyPreserveRule(bundle.preserveRule); - } else { - GlobalOptions.err.println("Warning: Can't find class " - + fullname); - return; - } - } - if (wildcard.matches(fullname)) { - if (GlobalOptions.verboseLevel > 1) - GlobalOptions.err.println("preserving "+ident); - ident.setPreserved(); - } - if (wildcard.startsWith(fullname+".")) { - if (ident instanceof PackageIdentifier) - ((PackageIdentifier) ident) - .preserveMatchingIdentifier(wildcard); - else - ((ClassIdentifier) ident) - .preserveMatchingIdentifier(wildcard); - } - } else { - String fullname = getFullName(); - if (fullname.length() > 0) - fullname += "."; - if (loadOnDemand) { - /* Load all matching classes and packages */ - Enumeration enum = - ClassInfo.getClassesAndPackages(getFullName()); - while (enum.hasMoreElements()) { - String subclazz = (String)enum.nextElement(); - if (loadedClasses.containsKey(subclazz)) - continue; - String subFull = fullname + subclazz; +// public void preserveMatchingIdentifier(IdentifierMatcher matcher) { +// String component = matcher.getNextComponent(getFullName()); +// if (component != null) { +// String fullname = getFullName(); +// fullname = (fullname.length() > 0) +// ? fullname + "."+ component : component; +// Identifier ident = (Identifier) loadedClasses.get(component); +// if (ident == null) { +// if (!loadOnDemand) { +// GlobalOptions.err.println +// ("Warning: Didn't load package/class "+ fullname); +// return; +// } +// if (ClassInfo.isPackage(fullname)) { +// ident = new PackageIdentifier(bundle, this, +// component, loadOnDemand); +// loadedClasses.put(component, ident); +// } else if (ClassInfo.exists(fullname)) { +// ident = new ClassIdentifier(this, component, +// ClassInfo.forName(fullname)); +// loadedClasses.put(component, ident); +// ((ClassIdentifier) ident).initClass(); +// if (bundle.preserveRule != null) +// ident.applyPreserveRule(bundle.preserveRule); +// } else { +// GlobalOptions.err.println("Warning: Can't find class " +// + fullname); +// return; +// } +// } +// if (matcher.matches(fullname)) { +// if (GlobalOptions.verboseLevel > 1) +// GlobalOptions.err.println("preserving "+ident); +// ident.setPreserved(); +// } +// if (matcher.startsWith(fullname+".")) { +// if (ident instanceof PackageIdentifier) +// ((PackageIdentifier) ident) +// .preserveMatchingIdentifier(matcher); +// else +// ((ClassIdentifier) ident) +// .preserveMatchingIdentifier(matcher); +// } +// } else { +// String fullname = getFullName(); +// if (fullname.length() > 0) +// fullname += "."; +// if (loadOnDemand) { +// /* Load all matching classes and packages */ +// Enumeration enum = +// ClassInfo.getClassesAndPackages(getFullName()); +// while (enum.hasMoreElements()) { +// String subclazz = (String)enum.nextElement(); +// if (loadedClasses.containsKey(subclazz)) +// continue; +// String subFull = fullname + subclazz; - if (wildcard.startsWith(subFull)) { - if (ClassInfo.isPackage(subFull)) { - System.err.println("is package: "+subFull); - Identifier ident = new PackageIdentifier - (bundle, this, subclazz, true); - loadedClasses.put(subclazz, ident); - } else { - ClassIdentifier ident = new ClassIdentifier - (this, subclazz, ClassInfo.forName(subFull)); - loadedClasses.put(subclazz, ident); - ((ClassIdentifier) ident).initClass(); - if (bundle.preserveRule != null) - ident.applyPreserveRule(bundle.preserveRule); - } - } - } - } - for (Iterator i = loadedClasses.values().iterator(); - i.hasNext(); ) { - Identifier ident = (Identifier) i.next(); - if (wildcard.matches(ident.getFullName())) { - if (GlobalOptions.verboseLevel > 1) - GlobalOptions.err.println("Preserving "+ident); - ident.setPreserved(); - } - if (wildcard.startsWith(ident.getFullName()+".")) { - if (ident instanceof PackageIdentifier) - ((PackageIdentifier) ident) - .preserveMatchingIdentifier(wildcard); - else - ((ClassIdentifier) ident) - .preserveMatchingIdentifier(wildcard); - } - } - } - } +// if (matcher.startsWith(subFull)) { +// if (ClassInfo.isPackage(subFull)) { +// System.err.println("is package: "+subFull); +// Identifier ident = new PackageIdentifier +// (bundle, this, subclazz, true); +// loadedClasses.put(subclazz, ident); +// } else { +// ClassIdentifier ident = new ClassIdentifier +// (this, subclazz, ClassInfo.forName(subFull)); +// loadedClasses.put(subclazz, ident); +// ((ClassIdentifier) ident).initClass(); +// if (bundle.preserveRule != null) +// ident.applyPreserveRule(bundle.preserveRule); +// } +// } +// } +// } +// for (Iterator i = loadedClasses.values().iterator(); +// i.hasNext(); ) { +// Identifier ident = (Identifier) i.next(); +// if (matcher.matches(ident.getFullName())) { +// if (GlobalOptions.verboseLevel > 1) +// GlobalOptions.err.println("Preserving "+ident); +// ident.setPreserved(); +// } +// if (matcher.startsWith(ident.getFullName()+".")) { +// if (ident instanceof PackageIdentifier) +// ((PackageIdentifier) ident) +// .preserveMatchingIdentifier(matcher); +// else +// ((ClassIdentifier) ident) +// .preserveMatchingIdentifier(matcher); +// } +// } +// } +// } /** * @return the full qualified name. diff --git a/jode/jode/obfuscator/ParseException.java b/jode/jode/obfuscator/ParseException.java new file mode 100644 index 0000000..5686baf --- /dev/null +++ b/jode/jode/obfuscator/ParseException.java @@ -0,0 +1,7 @@ +package jode.obfuscator; + +public class ParseException extends Exception { + public ParseException(int linenr, String message) { + super ("line "+linenr+": "+message); + } +} diff --git a/jode/jode/obfuscator/Renamer.java b/jode/jode/obfuscator/Renamer.java.in similarity index 92% rename from jode/jode/obfuscator/Renamer.java rename to jode/jode/obfuscator/Renamer.java.in index 2a1897f..6fd588c 100644 --- a/jode/jode/obfuscator/Renamer.java +++ b/jode/jode/obfuscator/Renamer.java.in @@ -18,6 +18,7 @@ */ package jode.obfuscator; +import @COLLECTIONS@.Iterator; public interface Renamer { @@ -27,5 +28,5 @@ public interface Renamer { * @param lastName the last name generated for this identifier, or * null, if no name was generated yet. */ - public String generateName(Identifier ident, String lastName); + public Iterator generateNames(Identifier ident); } diff --git a/jode/jode/obfuscator/ScriptParser.java.in b/jode/jode/obfuscator/ScriptParser.java.in new file mode 100644 index 0000000..76a5988 --- /dev/null +++ b/jode/jode/obfuscator/ScriptParser.java.in @@ -0,0 +1,258 @@ +package jode.obfuscator; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; + +import @COLLECTIONS@.Collection; +import @COLLECTIONS@.LinkedList; + +public class ScriptParser { + static int NO_TOKEN = -2; + static int EOF_TOKEN = -1; + static int STRING_TOKEN = 0; + static int NEW_TOKEN = 1; + static int EQUALS_TOKEN = 2; + static int COMMA_TOKEN = 3; + static int OPENBRACE_TOKEN = 4; + static int CLOSEBRACE_TOKEN = 5; + static int IDENTIFIER_TOKEN = 6; + static int NUMBER_TOKEN = 7; + Scanner scanner; + + class Scanner { + BufferedReader input; + String value; + String line; + int column; + int linenr; + int pushback = NO_TOKEN; + + public Scanner(Reader i) { + input = new BufferedReader(i); + } + + public void readString() throws ParseException { + StringBuffer val = new StringBuffer(); + while (column < line.length()) { + char c = line.charAt(column++); + if (c == '"') { + value = val.toString(); + return; + } + if (c == '\\') { + c = line.charAt(column++); + switch (c) { + case 'n': + val.append('\n'); + break; + case 't': + val.append('\t'); + break; + case 'r': + val.append('\r'); + break; + case 'u': + if (column+4 <= line.length()) { + try { + char uni = (char) Integer.parseInt + (line.substring(column, column+4), 16); + column += 4; + val.append(uni); + } catch (NumberFormatException ex) { + throw new ParseException + (linenr, + "Invalid unicode escape character"); + } + } else + throw new ParseException + (linenr, + "Invalid unicode escape character"); + break; + default: + val.append(c); + } + } else + val.append(c); + } + throw new ParseException(linenr, + "String spans over multiple lines"); + } + public void readIdentifier() { + int start = column-1; + while (column < line.length() + && Character.isUnicodeIdentifierPart(line.charAt(column))) + column++; + value = line.substring(start, column); + } + + public void readNumber() { + boolean hex = false; + int start = column-1; + /* special case for hex numbers */ + if (line.charAt(start) == '0' && line.charAt(column) == 'x') { + column++; + hex = true; + } + while (column < line.length()) { + char c = line.charAt(column); + if (!Character.isDigit(c)) { + if (!hex) + break; + if ((c < 'A' || c > 'F') && (c < 'a' || c > 'f')) + break; + } + column++; + } + value = line.substring(start, column); + } + + public void pushbackToken(int token) { + if (pushback != NO_TOKEN) + throw new IllegalStateException + ("Can only handle one pushback"); + pushback = token; + } + + public int getToken() throws ParseException, IOException { + if (pushback != NO_TOKEN) { + int result = pushback; + pushback = NO_TOKEN; + return result; + } + value = null; + while (true) { + if (line == null) { + line = input.readLine(); + if (line == null) + return EOF_TOKEN; + linenr++; + column = 0; + } + while (column < line.length()) { + char c = line.charAt(column++); + if (Character.isWhitespace(c)) + continue; + if (c == '#') + // this is a comment, skip this line + break; + if (c == '=') + return EQUALS_TOKEN; + if (c == ',') + return COMMA_TOKEN; + if (c == '{') + return OPENBRACE_TOKEN; + if (c == '}') + return CLOSEBRACE_TOKEN; + if (c == '"') { + readString(); + return STRING_TOKEN; + } + if (Character.isDigit(c) || c == '+' || c == '-') { + readNumber(); + return NUMBER_TOKEN; + } + if (Character.isUnicodeIdentifierStart(c)) { + readIdentifier(); + if (value.equals("new")) + return NEW_TOKEN; + return IDENTIFIER_TOKEN; + } + throw new ParseException + (linenr, "Illegal character `"+c+"'"); + } + line = null; + } + } + + public String getValue() { + return value; + } + + public int getLineNr() { + return linenr; + } + } + + public ScriptParser(Reader reader) { + this.scanner = new Scanner(reader); + } + + public Object parseClass() throws ParseException, IOException { + int linenr = scanner.getLineNr(); + int token = scanner.getToken(); + if (token != IDENTIFIER_TOKEN) + throw new ParseException(linenr, "Class name expected"); + Object instance; + try { + Class clazz = Class.forName("jode.obfuscator."+scanner.getValue()); + instance = clazz.newInstance(); + } catch (ClassNotFoundException ex) { + throw new ParseException(scanner.getLineNr(), + "Class `"+scanner.getValue() + +"' not found"); + } catch (Exception ex) { + throw new ParseException(scanner.getLineNr(), + "Class `"+scanner.getValue() + +"' not valid: "+ex.getMessage()); + } + + token = scanner.getToken(); + if (token == OPENBRACE_TOKEN) { + if (!(instance instanceof OptionHandler)) + throw new ParseException + (scanner.getLineNr(), + "Class `"+instance.getClass().getName() + +"' doesn't handle options."); + parseOptions((OptionHandler) instance); + if (scanner.getToken() != CLOSEBRACE_TOKEN) + throw new ParseException(scanner.getLineNr(), "`}' expected"); + } else + scanner.pushbackToken(token); + return instance; + } + + public void parseOptions(OptionHandler optionHandler) + throws ParseException, IOException + { + int token = scanner.getToken(); + while (true) { + if (token == EOF_TOKEN || token == CLOSEBRACE_TOKEN) { + scanner.pushbackToken(token); + return; + } + if (token != IDENTIFIER_TOKEN) + throw new ParseException(scanner.getLineNr(), + "identifier expected"); + String ident = scanner.getValue(); + if (scanner.getToken() != EQUALS_TOKEN) + throw new ParseException(scanner.getLineNr(), + "equal sign expected"); + + int linenr = scanner.getLineNr(); + Collection values = new LinkedList(); + do { + token = scanner.getToken(); + if (token == NEW_TOKEN) { + values.add(parseClass()); + } else if (token == STRING_TOKEN) { + values.add(scanner.getValue()); + } else if (token == NUMBER_TOKEN) { + values.add(new Integer(scanner.getValue())); + } + token = scanner.getToken(); + } while (token == COMMA_TOKEN); + try { + optionHandler.setOption(ident, values); + } catch (IllegalArgumentException ex) { + throw new ParseException(linenr, + optionHandler.getClass().getName() + +": "+ex.getMessage()); + } catch (RuntimeException ex) { + throw new ParseException(linenr, + optionHandler.getClass().getName() + +": Illegal value: "+ex.getMessage()); + } + } + } +} diff --git a/jode/jode/obfuscator/StrongRenamer.java b/jode/jode/obfuscator/StrongRenamer.java deleted file mode 100644 index 5e4cb8f..0000000 --- a/jode/jode/obfuscator/StrongRenamer.java +++ /dev/null @@ -1,63 +0,0 @@ -/* StrongRenamer Copyright (C) 1999 Jochen Hoenicke. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Id$ - */ - -package jode.obfuscator; - -public class StrongRenamer implements Renamer { - String charSetStart; - String charSetCont; - - public StrongRenamer(String charSetStart, String charSetCont) { - this.charSetStart = charSetStart; - this.charSetCont = charSetCont; - } - - public String generateName(Identifier ident, String lastName) { - if (lastName == null) - return charSetStart.substring(0,1); - - char firstCont = charSetCont.charAt(0); - int pos = lastName.length() - 1; - StringBuffer sb = new StringBuffer(lastName.length() + 1); - while (pos > 0) { - int index = charSetCont.indexOf(lastName.charAt(pos)) + 1; - if (index < charSetCont.length()) { - sb.append(lastName.substring(0, pos)); - sb.append(charSetCont.charAt(index)); - for (int i = lastName.length() - pos - 1; i-- > 0; ) - sb.append(firstCont); - return sb.toString(); - } - pos --; - } - - int index = charSetStart.indexOf(lastName.charAt(pos)) + 1; - if (index < charSetStart.length()) { - sb.append(charSetStart.charAt(index)); - for (int i = lastName.length() - 1; i-- > 0; ) - sb.append(firstCont); - return sb.toString(); - } else { - sb.append(charSetStart.charAt(0)); - for (int i = lastName.length(); i-- > 0; ) - sb.append(firstCont); - } - return sb.toString(); - } -} diff --git a/jode/jode/obfuscator/StrongRenamer.java.in b/jode/jode/obfuscator/StrongRenamer.java.in new file mode 100644 index 0000000..6d6a00d --- /dev/null +++ b/jode/jode/obfuscator/StrongRenamer.java.in @@ -0,0 +1,141 @@ +/* StrongRenamer Copyright (C) 1999 Jochen Hoenicke. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id$ + */ + +package jode.obfuscator; +import @COLLECTIONS@.Collection; +import @COLLECTIONS@.Iterator; +import @COLLECTIONEXTRA@.UnsupportedOperationException; + +public class StrongRenamer implements Renamer, OptionHandler { + static String[] idents = { + "Package", "Class", "Field", "Method", "Local" + }; + static String[] parts = { + "Start", "Part" + }; + String charsets[][]; + + public StrongRenamer() { + charsets = new String[idents.length][parts.length]; + for (int i=0; i< idents.length; i++) + for (int j=0; j< parts.length; j++) + charsets[i][j] = "abcdefghijklmnopqrstuvwxyz"; + } + + public void setOption(String option, Collection values) { + if (option.startsWith("charset")) { + Object value = values.iterator().next(); + if (values.size() != 1 || !(value instanceof String)) + throw new IllegalArgumentException + ("Only string parameter are supported."); + String set = (String) value; + String remOpt = option.substring("charset".length()); + int part = -1, ident = -1; + if (remOpt.length() > 0) { + for (int i=0; i < idents.length; i++) { + if (remOpt.startsWith(idents[i])) { + remOpt = option.substring(idents[i].length()); + ident = i; + break; + } + } + } + if (remOpt.length() > 0) { + for (int j=0; j < parts.length; j++) { + if (remOpt.startsWith(parts[j])) { + remOpt = option.substring(parts[j].length()); + part = j; + break; + } + } + } + if (remOpt.length() > 0) + throw new IllegalArgumentException("Invalid charset `" + +option+"'"); + for (int i = 0; i < idents.length; i++) { + if (ident >= 0 && ident != i) + continue; + for (int j = 0; j < parts.length; j++) { + if (part >= 0 && part != j) + continue; + charsets[i][j] = set; + } + } + } else + throw new IllegalArgumentException("Invalid option `" + +option+"'"); + } + + public Iterator generateNames(Identifier ident) { + final String[] currCharset; + if (ident instanceof PackageIdentifier) + currCharset = charsets[0]; + else if (ident instanceof PackageIdentifier) + currCharset = charsets[1]; + else if (ident instanceof ClassIdentifier) + currCharset = charsets[2]; + else if (ident instanceof FieldIdentifier) + currCharset = charsets[3]; + else if (ident instanceof MethodIdentifier) + currCharset = charsets[4]; + else if (ident instanceof LocalIdentifier) + currCharset = charsets[5]; + else + throw new IllegalArgumentException(ident.getClass().getName()); + + return new Iterator() { + char[] name = null; + + public boolean hasNext() { + return true; + } + public Object next() { + if (name == null) { + name = new char[] { currCharset[0].charAt(0) }; + return new String(name); + } + + int pos = name.length - 1; + String charset = currCharset[1]; + while (pos >= 0) { + if (pos == 0) + charset = currCharset[0]; + + int index = charset.indexOf(name[pos]) + 1; + if (index < charset.length()) { + name[pos] = charset.charAt(index); + return new String(name); + } + name[pos--] = charset.charAt(0); + } + + name = new char[name.length+1]; + name[0] = currCharset[0].charAt(0); + char firstCont = currCharset[1].charAt(0); + for (int i=1; i 0) + prefix += "."; + int lastDot = prefix.length(); if (!wildcard.startsWith(prefix)) return null; - if (lastDot > 0) { - if (wildcard.charAt(lastDot++) != '.') - return null; - } int nextDot = wildcard.indexOf('.', lastDot); if (nextDot > 0 @@ -48,15 +63,25 @@ public class WildCard { return null; } - public boolean startsWith(String test) { - if (firstStar == -1 || firstStar >= test.length()) - return wildcard.startsWith(test); - return test.startsWith(wildcard.substring(0, firstStar)); + public boolean matchesSub(Identifier ident, String subident) { + String prefix = ident.getFullName(); + if (prefix.length() > 0) + prefix += "."; + if (subident != null) + prefix += subident; + if (firstStar == -1 || firstStar >= prefix.length()) + return wildcard.startsWith(prefix); + return prefix.startsWith(wildcard.substring(0, firstStar)); } - public boolean matches(String test) { - if (firstStar == -1) - return wildcard.equals(test); + public boolean matches(Identifier ident) { + String test = ident.getFullName(); + if (firstStar == -1) { + if (wildcard.equals(test)) { + return true; + } + return false; + } if (!test.startsWith(wildcard.substring(0, firstStar))) return false;