obfuscator scripting implemented

git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@1090 379699f6-c40d-0410-875b-85095c16579e
branch_1_1
jochen 25 years ago
parent f7be2d00d5
commit 2f04c0e0cb
  1. 6
      jode/configure.in
  2. 2
      jode/jode/Decompiler.java
  3. 10
      jode/jode/Makefile.am
  4. 26
      jode/jode/jode.jodescript
  5. 235
      jode/jode/obfuscator/ClassBundle.java.in
  6. 17
      jode/jode/obfuscator/ClassIdentifier.java.in
  7. 14
      jode/jode/obfuscator/ConstantAnalyzer.java.in
  8. 5
      jode/jode/obfuscator/Identifier.java.in
  9. 19
      jode/jode/obfuscator/IdentifierMatcher.java
  10. 349
      jode/jode/obfuscator/Main.java.in
  11. 1
      jode/jode/obfuscator/Makefile.am
  12. 28
      jode/jode/obfuscator/MethodIdentifier.java.in
  13. 8
      jode/jode/obfuscator/ModifierMatcher.java
  14. 109
      jode/jode/obfuscator/MultiIdentifierMatcher.java.in
  15. 99
      jode/jode/obfuscator/NameSwapper.java.in
  16. 8
      jode/jode/obfuscator/OptionHandler.java.in
  17. 284
      jode/jode/obfuscator/PackageIdentifier.java.in
  18. 7
      jode/jode/obfuscator/ParseException.java
  19. 3
      jode/jode/obfuscator/Renamer.java.in
  20. 258
      jode/jode/obfuscator/ScriptParser.java.in
  21. 63
      jode/jode/obfuscator/StrongRenamer.java
  22. 141
      jode/jode/obfuscator/StrongRenamer.java.in
  23. 21
      jode/jode/obfuscator/UniqueRenamer.java.in
  24. 51
      jode/jode/obfuscator/WildCard.java.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

@ -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 <path> "+

@ -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.<init>.*' \
--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

@ -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.<init>.()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.<init>.()V",
# "jode.JodeWindow.main.*",
# "jode.obfuscator.Main.main.*",
# "jode.swingui.Main.main.*"
analyzer = new SimpleAnalyzer
post = new LocalOptimizer, new RemovePopAnalyzer

@ -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();
}
}

@ -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();

@ -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);

@ -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();
}

@ -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);
}

@ -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 <classpath> "+
"The class path; should contain classes.zip");
err.println("\t-d,--dest <directory> "+
"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 <name> "+
"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 <file> "+
"Read (some) translation table from file");
err.println("\t--revtable <file> "+
"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 <path> "+
"search for classes in specified classpath.");
err.println(" "+
"The directories should be separated by ','.");
err.println(" -d, --dest <dir> "+
"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<params.length && params[i].startsWith("-"); i++) {
if (params[i].equals("-v"))
GlobalOptions.verboseLevel++;
else if (params[i].startsWith("--debug")) {
String flags;
if (params[i].startsWith("--debug=")) {
flags = params[i].substring(8);
} else if (params[i].length() != 7) {
usage();
return;
} else {
flags = params[++i];
if (params.length == 0) {
usage();
return;
}
String cp = null, dest = null;
GlobalOptions.err.println(GlobalOptions.copyright);
bundle = new ClassBundle();
boolean errorInParams = false;
Getopt g = new Getopt("jode.obfuscator.Main", params, "hVvc:d:D:",
longOptions, true);
for (int opt = g.getopt(); opt != -1; opt = g.getopt()) {
switch(opt) {
case 0:
break;
case 'h':
usage();
errorInParams = true;
break;
case 'V':
GlobalOptions.err.println(GlobalOptions.version);
break;
case 'c':
cp = g.getOptarg();
break;
case 'd':
dest = g.getOptarg();
break;
case 'v': {
String arg = g.getOptarg();
if (arg == null)
GlobalOptions.verboseLevel++;
else {
try {
GlobalOptions.verboseLevel = Integer.parseInt(arg);
} catch (NumberFormatException ex) {
GlobalOptions.err.println
("jode.Decompiler: Argument `"
+arg+"' to --verbose must be numeric:");
errorInParams = true;
}
}
GlobalOptions.setDebugging(flags);
} else if (params[i].equals("--strip"))
setStripOptions(params[++i]);
else if (params[i].startsWith("--strip="))
setStripOptions(params[i].substring(8));
else if (params[i].equals("--sourcepath")
|| params[i].equals("--classpath")
|| params[i].equals("--cp"))
sourcePath = params[++i];
else if (params[i].equals("--dest")
|| params[i].equals("-d"))
destPath = params[++i];
/* Preserve options */
else if (params[i].equals("--package"))
preserveRule = ModifierMatcher.allowAll.forceAccess
(0, true);
else if (params[i].equals("--protected"))
preserveRule = ModifierMatcher.allowAll.forceAccess
(Modifier.PROTECTED, true);
else if (params[i].equals("--public"))
preserveRule = ModifierMatcher.allowAll.forceAccess
(Modifier.PUBLIC, true);
else if (params[i].equals("--preserve")) {
String ident = params[++i];
preservedIdents.add(ident);
}
else if (params[i].equals("--breakserial"))
options &= ~OPTION_PRESERVESERIAL;
break;
}
case 'D': {
String arg = g.getOptarg();
if (arg == null)
arg = "help";
errorInParams |= !GlobalOptions.setDebugging(arg);
break;
}
default:
errorInParams = true;
break;
}
}
if (errorInParams)
return;
/* Obfuscate options */
else if (params[i].equals("--rename"))
renamer = getRenamer(params[++i]);
else if (params[i].startsWith("--rename="))
renamer = getRenamer(params[i].substring(9));
else if (params[i].equals("--table")) {
table = params[++i];
}
else if (params[i].equals("--revtable")) {
toTable = params[++i];
}
else if (params[i].equals("--swaporder"))
swapOrder = true;
else if (params[i].equals("--")) {
i++;
break;
} else {
if (!params[i].equals("-h") && !params[i].equals("--help"))
GlobalOptions.err.println("Unknown option: "+params[i]);
usage();
return;
}
}
if (renamer == null)
renamer = getRenamer("none");
if (i == params.length) {
GlobalOptions.err.println("No package or classes specified.");
usage();
if (g.getOptind() != params.length - 1) {
GlobalOptions.err.println("You must specify exactly one script.");
return;
}
GlobalOptions.err.println("Loading classes");
ClassInfo.setClassPath(sourcePath);
bundle = new ClassBundle();
for (; i< params.length; i++)
bundle.loadClasses(params[i]);
GlobalOptions.err.println("Computing reachable / preserved settings");
bundle.setPreserved(preserveRule, preservedIdents);
try {
ScriptParser parser = new ScriptParser
(new FileReader(params[g.getOptind()]));
parser.parseOptions(bundle);
} catch (IOException ex) {
GlobalOptions.err.println
("IOException while reading script file.");
ex.printStackTrace(GlobalOptions.err);
return;
} catch (ParseException ex) {
GlobalOptions.err.println("Syntax error in script file: ");
GlobalOptions.err.println(ex.getMessage());
if (GlobalOptions.verboseLevel > 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();
}
}

@ -23,6 +23,7 @@ MY_JAVA_FILES = \
Main.java \
MethodIdentifier.java \
ModifierMatcher.java \
MultiIdentifierMatcher.java \
NameSwapper.java \
OptionHandler.java \
PackageIdentifier.java \

@ -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);

@ -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();

@ -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;
}
}

@ -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);
}
}

@ -0,0 +1,8 @@
package jode.obfuscator;
import @COLLECTIONS@.Collection;
public interface OptionHandler {
public void setOption(String option, Collection values)
throws IllegalArgumentException;
}

@ -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.

@ -0,0 +1,7 @@
package jode.obfuscator;
public class ParseException extends Exception {
public ParseException(int linenr, String message) {
super ("line "+linenr+": "+message);
}
}

@ -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);
}

@ -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());
}
}
}
}

@ -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();
}
}

@ -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 <name.length; i++)
name[i] = firstCont;
return new String(name);
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
}

@ -0,0 +1,21 @@
package jode.obfuscator;
import @COLLECTIONS@.Iterator;
public class UniqueRenamer implements Renamer {
static int serialnr = 0;
public Iterator generateNames(Identifier ident) {
return new Iterator() {
public boolean hasNext() {
return true;
}
public Object next() {
return "xxx" + serialnr++;
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
}

@ -18,25 +18,40 @@
*/
package jode.obfuscator;
import @COLLECTIONS@.Collection;
public class WildCard {
public class WildCard implements IdentifierMatcher, OptionHandler {
String wildcard;
int firstStar;
public WildCard() {
}
public WildCard(String wild) {
wildcard = wild;
firstStar = wildcard.indexOf('*');
}
public String getNextComponent(String prefix) {
public void setOption(String option, Collection values) {
if (option.equals("value")) {
if (values.size() != 1)
throw new IllegalArgumentException
("Wildcard supports only one value.");
wildcard = (String) values.iterator().next();
firstStar = wildcard.indexOf('*');
} else
throw new IllegalArgumentException("Invalid option `"+option+"'.");
}
public String getNextComponent(Identifier ident) {
String prefix = ident.getFullName();
if (prefix.length() > 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;
Loading…
Cancel
Save