*** empty log message ***

git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@252 379699f6-c40d-0410-875b-85095c16579e
stable
jochen 26 years ago
parent 2d01111d8f
commit 3a71d9649d
  1. 4
      jode/jode/bytecode/BinaryInfo.java
  2. 8
      jode/jode/flow/SpecialBlock.java
  3. 5
      jode/jode/obfuscator/ClassBundle.java
  4. 279
      jode/jode/obfuscator/ClassIdentifier.java
  5. 59
      jode/jode/obfuscator/FieldIdentifier.java
  6. 11
      jode/jode/obfuscator/Identifier.java
  7. 622
      jode/jode/obfuscator/MethodIdentifier.java
  8. 36
      jode/jode/obfuscator/PackageIdentifier.java

@ -43,6 +43,10 @@ public class BinaryInfo {
return null; return null;
} }
public AttributeInfo[] getAttributes() {
return attributes;
}
protected void skipAttributes(DataInputStream input) throws IOException { protected void skipAttributes(DataInputStream input) throws IOException {
int count = input.readUnsignedShort(); int count = input.readUnsignedShort();
for (int i=0; i< count; i++) { for (int i=0; i< count; i++) {

@ -146,6 +146,9 @@ public class SpecialBlock extends StructuredBlock {
if (last.outer instanceof SequentialBlock if (last.outer instanceof SequentialBlock
&& last.outer.getSubBlocks()[0] instanceof InstructionBlock) { && last.outer.getSubBlocks()[0] instanceof InstructionBlock) {
if (jump != null && jump.destination == null)
return false;
InstructionBlock prev InstructionBlock prev
= (InstructionBlock) last.outer.getSubBlocks()[0]; = (InstructionBlock) last.outer.getSubBlocks()[0];
Expression instr = prev.getInstruction(); Expression instr = prev.getInstruction();
@ -193,9 +196,8 @@ public class SpecialBlock extends StructuredBlock {
(new CompareBinaryOperator(instr.getType(), (new CompareBinaryOperator(instr.getType(),
Operator.EQUALS_OP), Operator.EQUALS_OP),
new Expression[] { previnstr, instr }); new Expression[] { previnstr, instr });
ConditionalBlock newIfThen = new ConditionalBlock(newCond); IfThenElseBlock newIfThen = new IfThenElseBlock(newCond);
newIfThen.moveDefinitions(last.outer.outer, newIfThen); newIfThen.setThenBlock(new EmptyBlock());
newIfThen.trueBlock.copyJump(jump);
newIfThen.moveJump(jump); newIfThen.moveJump(jump);
if (this == last) { if (this == last) {
newIfThen.replace(last.outer.outer); newIfThen.replace(last.outer.outer);

@ -90,6 +90,11 @@ public class ClassBundle {
} }
public void storeClasses(String destination) { public void storeClasses(String destination) {
File directory = new File(destination);
if (!directory.exists()) {
Obfuscator.err.println("Destination directory "
+directory.getPath()+" doesn't exists.");
}
basePackage.storeClasses(new File(destination)); basePackage.storeClasses(new File(destination));
} }
} }

@ -19,9 +19,7 @@
package jode.obfuscator; package jode.obfuscator;
import jode.Obfuscator; import jode.Obfuscator;
import jode.bytecode.ClassInfo; import jode.bytecode.*;
import jode.bytecode.FieldInfo;
import jode.bytecode.MethodInfo;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.*; import java.util.*;
import java.io.*; import java.io.*;
@ -31,8 +29,13 @@ public class ClassIdentifier extends Identifier {
PackageIdentifier pack; PackageIdentifier pack;
String name; String name;
ClassInfo info; ClassInfo info;
boolean willStrip;
int preserveRule; int preserveRule;
int fieldCount;
/* The first fieldCount are of type FieldIdentifier, the remaining
* are MethodIdentifier
*/
Identifier[] identifiers; Identifier[] identifiers;
Vector knownSubClasses = new Vector(); Vector knownSubClasses = new Vector();
Vector virtualReachables = new Vector(); Vector virtualReachables = new Vector();
@ -115,9 +118,8 @@ public class ClassIdentifier extends Identifier {
ClassIdentifier superident = (ClassIdentifier) ClassIdentifier superident = (ClassIdentifier)
bundle.getIdentifier(superclass.getName()); bundle.getIdentifier(superclass.getName());
if (superident != null) { if (superident != null) {
for (int i=0; i < superident.identifiers.length; i++) for (int i=superident.fieldCount;
if (superident.identifiers[i] i < superident.identifiers.length; i++) {
instanceof MethodIdentifier) {
MethodIdentifier mid = (MethodIdentifier) MethodIdentifier mid = (MethodIdentifier)
superident.identifiers[i]; superident.identifiers[i];
// all virtual methods in superclass must be chained. // all virtual methods in superclass must be chained.
@ -159,15 +161,20 @@ public class ClassIdentifier extends Identifier {
} }
public void setSingleReachable() { public void setSingleReachable() {
info.loadInfo(info.FULLINFO); super.setSingleReachable();
if (Obfuscator.isVerbose) if (Obfuscator.isVerbose)
Obfuscator.err.println(this); Obfuscator.err.println("Reachable: "+this);
reachableIdentifier("<clinit>", "()V", false);
}
public void initClass() {
info.loadInfo(info.FULLINFO);
FieldInfo[] finfos = info.getFields(); FieldInfo[] finfos = info.getFields();
MethodInfo[] minfos = info.getMethods(); MethodInfo[] minfos = info.getMethods();
fieldCount = finfos.length;
identifiers = new Identifier[finfos.length + minfos.length]; identifiers = new Identifier[finfos.length + minfos.length];
for (int i=0; i< finfos.length; i++) { for (int i=0; i< fieldCount; i++) {
identifiers[i] = new FieldIdentifier(this, finfos[i]); identifiers[i] = new FieldIdentifier(this, finfos[i]);
if ((preserveRule & (finfos[i].getModifiers() ^ Modifier.PRIVATE)) if ((preserveRule & (finfos[i].getModifiers() ^ Modifier.PRIVATE))
!= 0) { != 0) {
@ -176,7 +183,7 @@ public class ClassIdentifier extends Identifier {
} }
} }
for (int i=0; i< minfos.length; i++) { for (int i=0; i< minfos.length; i++) {
identifiers[finfos.length + i] identifiers[fieldCount + i]
= new MethodIdentifier(this, minfos[i]); = new MethodIdentifier(this, minfos[i]);
if ((preserveRule & (minfos[i].getModifiers() ^ Modifier.PRIVATE)) if ((preserveRule & (minfos[i].getModifiers() ^ Modifier.PRIVATE))
!= 0) { != 0) {
@ -192,7 +199,6 @@ public class ClassIdentifier extends Identifier {
ClassIdentifier ifaceident = (ClassIdentifier) ClassIdentifier ifaceident = (ClassIdentifier)
bundle.getIdentifier(ifaces[i].getName()); bundle.getIdentifier(ifaces[i].getName());
if (ifaceident != null) { if (ifaceident != null) {
ifaceident.setReachable();
ifaceident.addSubClass(this); ifaceident.addSubClass(this);
} }
initSuperClasses(ifaces[i]); initSuperClasses(ifaces[i]);
@ -202,37 +208,29 @@ public class ClassIdentifier extends Identifier {
ClassIdentifier superident = (ClassIdentifier) ClassIdentifier superident = (ClassIdentifier)
bundle.getIdentifier(info.getSuperclass().getName()); bundle.getIdentifier(info.getSuperclass().getName());
if (superident != null) { if (superident != null) {
superident.setReachable();
superident.addSubClass(this); superident.addSubClass(this);
} }
initSuperClasses(info.getSuperclass()); initSuperClasses(info.getSuperclass());
} }
preserveIdentifier("<clinit>", "()V"); preserveIdentifier("<clinit>", "()V");
preserveIdentifier("<init>", null);
} }
public void strip() { public void strip() {
int newLength = 0; willStrip = true;
if (Obfuscator.isDebugging) {
for (int i=0; i < identifiers.length; i++) { for (int i=0; i < identifiers.length; i++) {
if (identifiers[i].isReachable()) if (!identifiers[i].isReachable())
newLength++;
else {
if (Obfuscator.isDebugging)
Obfuscator.err.println(identifiers[i].toString() Obfuscator.err.println(identifiers[i].toString()
+ " is stripped"); + " is stripped");
} }
} }
Identifier[] newIdents = new Identifier[newLength];
int index = 0;
for (int i=0; i < identifiers.length; i++) {
if (identifiers[i].isReachable())
newIdents[index++] = identifiers[i];
}
identifiers = newIdents;
} }
public void buildTable(int renameRule) { public void buildTable(int renameRule) {
super.buildTable(renameRule); super.buildTable(renameRule);
for (int i=0; i < identifiers.length; i++) for (int i=0; i < identifiers.length; i++)
if (!willStrip || identifiers[i].isReachable())
identifiers[i].buildTable(renameRule); identifiers[i].buildTable(renameRule);
} }
@ -240,11 +238,122 @@ public class ClassIdentifier extends Identifier {
if (getName() != getAlias()) if (getName() != getAlias())
out.println("" + getFullAlias() + " = " + getName()); out.println("" + getFullAlias() + " = " + getName());
for (int i=0; i < identifiers.length; i++) for (int i=0; i < identifiers.length; i++)
if (!willStrip || identifiers[i].isReachable())
identifiers[i].writeTable(out); identifiers[i].writeTable(out);
} }
public void storeClass(OutputStream out) throws IOException { public void addIfaces(Vector result, ClassInfo[] ifaces) {
/*XXX*/ for (int i=0; i < ifaces.length; i++) {
ClassIdentifier ifaceident = (ClassIdentifier)
bundle.getIdentifier(ifaces[i].getName());
if (ifaceident != null) {
if (ifaceident.isReachable())
result.addElement(ifaceident.getFullAlias());
else
addIfaces(result, ifaceident.info.getInterfaces());
} else
result.addElement(ifaces[i].getName());
}
}
/**
* Generates the new super class and interfaces, removing super
* classes and interfaces that are not reachable.
* @return an array of class names (full qualified, dot separated)
* where the first entry is the super class (may be null) and the
* other entries are the interfaces.
*/
public String[] getSuperIfaces() {
/* First generate Ifaces and superclass infos */
Vector newSuperIfaces = new Vector();
addIfaces(newSuperIfaces, info.getInterfaces());
String superName = null;
ClassInfo superClass = info.getSuperclass();
while (superClass != null) {
ClassIdentifier superident = (ClassIdentifier)
bundle.getIdentifier(superClass.getName());
if (superident != null) {
if (superident.isReachable()) {
superName = superident.getFullAlias();
break;
} else {
addIfaces(newSuperIfaces, superClass.getInterfaces());
superClass = superClass.getSuperclass();
}
} else {
superName = superClass.getName();
break;
}
}
newSuperIfaces.insertElementAt(superName, 0);
String[] result = new String[newSuperIfaces.size()];
newSuperIfaces.copyInto(result);
return result;
}
public void storeClass(DataOutputStream out) throws IOException {
GrowableConstantPool gcp = new GrowableConstantPool();
for (int i=fieldCount; i < identifiers.length; i++)
if (!willStrip || identifiers[i].isReachable())
((MethodIdentifier)identifiers[i]).reserveSmallConstants(gcp);
int[] hierarchyInts;
{
String[] hierarchy = getSuperIfaces();
hierarchyInts = new int[hierarchy.length];
for (int i=0; i< hierarchy.length; i++) {
if (hierarchy[i] != null)
hierarchyInts[i] = gcp.putClassRef(hierarchy[i]);
else
hierarchyInts[i] = 0;
}
hierarchy = null;
}
int nameIndex = gcp.putClassRef(getFullAlias());
int fields = 0;
int methods = 0;
for (int i=0; i<fieldCount; i++) {
if (!willStrip || identifiers[i].isReachable()) {
((FieldIdentifier)identifiers[i]).fillConstantPool(gcp);
fields++;
}
}
for (int i=fieldCount; i < identifiers.length; i++) {
if (!willStrip || identifiers[i].isReachable()) {
((MethodIdentifier)identifiers[i]).fillConstantPool(gcp);
methods++;
}
}
out.writeInt(0xcafebabe);
out.writeShort(3);
out.writeShort(45);
gcp.write(out);
out.writeShort(info.getModifiers());
out.writeShort(nameIndex);
out.writeShort(hierarchyInts[0]);
out.writeShort(hierarchyInts.length-1);
for (int i=1; i<hierarchyInts.length; i++)
out.writeShort(hierarchyInts[i]);
out.writeShort(fields);
for (int i=0; i<fieldCount; i++)
if (!willStrip || identifiers[i].isReachable())
((FieldIdentifier)identifiers[i]).write(out);
out.writeShort(methods);
for (int i=fieldCount; i < identifiers.length; i++)
if (!willStrip || identifiers[i].isReachable())
((MethodIdentifier)identifiers[i]).write(out);
out.writeShort(0); // no attributes;
}
public Identifier getParent() {
return pack;
} }
/** /**
@ -273,30 +382,66 @@ public class ClassIdentifier extends Identifier {
return "ClassIdentifier "+getFullName(); return "ClassIdentifier "+getFullName();
} }
public static boolean containsField(ClassInfo clazz, String newAlias) { public Identifier getIdentifier(String fieldName, String typeSig) {
for (int i=0; i < identifiers.length; i++) {
if (identifiers[i].getName().equals(fieldName)
&& identifiers[i].getType().startsWith(typeSig))
return identifiers[i];
}
ClassInfo[] ifaces = info.getInterfaces();
for (int i=0; i < ifaces.length; i++) {
ClassIdentifier ifaceident = (ClassIdentifier)
bundle.getIdentifier(ifaces[i].getName());
if (ifaceident != null) {
Identifier ident
= ifaceident.getIdentifier(fieldName, typeSig);
if (ident != null)
return ident;
}
}
if (info.getSuperclass() != null) {
ClassIdentifier superident = (ClassIdentifier)
bundle.getIdentifier(info.getSuperclass().getName());
if (superident != null) {
Identifier ident
= superident.getIdentifier(fieldName, typeSig);
if (ident != null)
return ident;
}
}
return null;
}
public static boolean containFieldAlias(ClassInfo clazz,
String fieldName, String typeSig) {
FieldInfo[] finfos = clazz.getFields(); FieldInfo[] finfos = clazz.getFields();
for (int i=0; i< finfos.length; i++) { for (int i=0; i< finfos.length; i++) {
if (finfos[i].getName().equals(newAlias)) if (finfos[i].getName().equals(fieldName)
&& finfos[i].getType().getTypeSignature().startsWith(typeSig))
return true; return true;
} }
ClassInfo[] ifaces = clazz.getInterfaces(); ClassInfo[] ifaces = clazz.getInterfaces();
for (int i=0; i < ifaces.length; i++) { for (int i=0; i < ifaces.length; i++) {
if (containsField(ifaces[i], newAlias)) if (containFieldAlias(ifaces[i], fieldName, typeSig))
return true; return true;
} }
if (clazz.getSuperclass() != null) { if (clazz.getSuperclass() != null) {
if (containsField(clazz.getSuperclass(), newAlias)) if (containFieldAlias(clazz.getSuperclass(),
fieldName, typeSig))
return true; return true;
} }
return false; return false;
} }
public boolean containsField(String newAlias) { public boolean containFieldAlias(String fieldName, String typeSig) {
for (int i=0; i< identifiers.length; i++) { for (int i=0; i < fieldCount; i++) {
if (identifiers[i] instanceof FieldIdentifier if ((!willStrip || identifiers[i].isReachable())
&& identifiers[i].getAlias().equals(newAlias)) && identifiers[i].getAlias().equals(fieldName)
&& identifiers[i].getType().startsWith(typeSig))
return true; return true;
} }
@ -305,10 +450,10 @@ public class ClassIdentifier extends Identifier {
ClassIdentifier ifaceident = (ClassIdentifier) ClassIdentifier ifaceident = (ClassIdentifier)
bundle.getIdentifier(ifaces[i].getName()); bundle.getIdentifier(ifaces[i].getName());
if (ifaceident != null) { if (ifaceident != null) {
if (ifaceident.containsField(newAlias)) if (ifaceident.containFieldAlias(fieldName, typeSig))
return true; return true;
} else { } else {
if (containsField(ifaces[i], newAlias)) if (containFieldAlias(ifaces[i], fieldName, typeSig))
return true; return true;
} }
} }
@ -317,45 +462,49 @@ public class ClassIdentifier extends Identifier {
ClassIdentifier superident = (ClassIdentifier) ClassIdentifier superident = (ClassIdentifier)
bundle.getIdentifier(info.getSuperclass().getName()); bundle.getIdentifier(info.getSuperclass().getName());
if (superident != null) { if (superident != null) {
if (superident.containsField(newAlias)) if (superident.containFieldAlias(fieldName, typeSig))
return true; return true;
} else { } else {
if (containsField(info.getSuperclass(), newAlias)) if (containFieldAlias(info.getSuperclass(),
fieldName, typeSig))
return true; return true;
} }
} }
return false; return false;
} }
public static boolean containsMethod(ClassInfo clazz, public static Object getMethod(ClassInfo clazz,
String newAlias, String paramType) { String methodName, String paramType) {
MethodInfo[] minfos = clazz.getMethods(); MethodInfo[] minfos = clazz.getMethods();
for (int i=0; i< minfos.length; i++) { for (int i=0; i< minfos.length; i++) {
if (minfos[i].getName().equals(newAlias) if (minfos[i].getName().equals(methodName)
&& minfos[i].getType().getTypeSignature() && minfos[i].getType().getTypeSignature()
.startsWith(paramType)) .startsWith(paramType))
return true; return minfos[i];
} }
ClassInfo[] ifaces = clazz.getInterfaces(); ClassInfo[] ifaces = clazz.getInterfaces();
for (int i=0; i < ifaces.length; i++) { for (int i=0; i < ifaces.length; i++) {
if (containsMethod(ifaces[i], newAlias, paramType)) Object result = getMethod(ifaces[i], methodName, paramType);
return true; if (result != null)
return result;
} }
if (clazz.getSuperclass() != null) { if (clazz.getSuperclass() != null) {
if (containsMethod(clazz.getSuperclass(), newAlias, paramType)) Object result = getMethod(clazz.getSuperclass(),
return true; methodName, paramType);
if (result != null)
return result;
} }
return false; return null;
} }
public boolean containsMethod(String newAlias, String paramType) { public Object getMethod(String methodName, String paramType) {
for (int i=0; i< identifiers.length; i++) { for (int i=fieldCount; i< identifiers.length; i++) {
if (identifiers[i] instanceof MethodIdentifier if ((!willStrip || identifiers[i].isReachable())
&& identifiers[i].getAlias().equals(newAlias) && identifiers[i].getAlias().equals(methodName)
&& identifiers[i].getType().startsWith(paramType)) && identifiers[i].getType().startsWith(paramType))
return true; return identifiers[i];
} }
ClassInfo[] ifaces = info.getInterfaces(); ClassInfo[] ifaces = info.getInterfaces();
@ -363,11 +512,13 @@ public class ClassIdentifier extends Identifier {
ClassIdentifier ifaceident = (ClassIdentifier) ClassIdentifier ifaceident = (ClassIdentifier)
bundle.getIdentifier(ifaces[i].getName()); bundle.getIdentifier(ifaces[i].getName());
if (ifaceident != null) { if (ifaceident != null) {
if (ifaceident.containsMethod(newAlias, paramType)) Object result = ifaceident.getMethod(methodName, paramType);
return true; if (result != null)
return result;
} else { } else {
if (containsMethod(ifaces[i], newAlias, paramType)) Object result = getMethod(ifaces[i], methodName, paramType);
return true; if (result != null)
return result;
} }
} }
@ -375,17 +526,21 @@ public class ClassIdentifier extends Identifier {
ClassIdentifier superident = (ClassIdentifier) ClassIdentifier superident = (ClassIdentifier)
bundle.getIdentifier(info.getSuperclass().getName()); bundle.getIdentifier(info.getSuperclass().getName());
if (superident != null) { if (superident != null) {
if (superident.containsMethod(newAlias, paramType)) Object result = superident.getMethod(methodName, paramType);
return true; if (result != null)
return result;
} else { } else {
if (containsMethod(info.getSuperclass(), newAlias, paramType)) Object result = getMethod(info.getSuperclass(),
return true; methodName, paramType);
if (result != null)
return result;
} }
} }
return false; return null;
} }
public boolean conflicting(String newAlias) { public boolean conflicting(String newAlias) {
return pack.contains(newAlias); return pack.contains(newAlias);
} }
} }

@ -17,7 +17,8 @@
* $Id$ * $Id$
*/ */
package jode.obfuscator; package jode.obfuscator;
import jode.bytecode.FieldInfo; import jode.bytecode.*;
import java.io.*;
public class FieldIdentifier extends Identifier{ public class FieldIdentifier extends Identifier{
FieldInfo info; FieldInfo info;
@ -29,6 +30,21 @@ public class FieldIdentifier extends Identifier{
this.clazz = clazz; this.clazz = clazz;
} }
public void setSingleReachable() {
super.setSingleReachable();
String type = getType();
int index = type.indexOf('L');
if (index != -1) {
int end = type.indexOf(';', index);
clazz.bundle.reachableIdentifier(type.substring(index+1, end)
, false);
}
}
public Identifier getParent() {
return clazz;
}
public String getFullName() { public String getFullName() {
return clazz.getFullName() + "." + getName(); return clazz.getFullName() + "." + getName();
} }
@ -50,7 +66,44 @@ public class FieldIdentifier extends Identifier{
} }
public boolean conflicting(String newAlias) { public boolean conflicting(String newAlias) {
return clazz.containsField(newAlias) return clazz.containFieldAlias(newAlias, "")
|| clazz.containsMethod(newAlias, ""); || (clazz.getMethod(newAlias, "") != null);
}
int nameIndex;
int descriptorIndex;
int constvalIndex;
int constvalcontentIndex;
public void fillConstantPool(GrowableConstantPool gcp)
throws ClassFormatException {
nameIndex = gcp.putUTF(getAlias());
descriptorIndex = gcp.putUTF(clazz.bundle.getTypeAlias(getType()));
constvalIndex = 0;
AttributeInfo attribute = info.findAttribute("ConstantValue");
if (attribute != null) {
byte[] contents = attribute.getContents();
if (contents.length != 2)
throw new ClassFormatError("ConstantValue attribute"
+ " has wrong length");
int index = (contents[0] & 0xff) << 8 | (contents[1] & 0xff);
constvalIndex = gcp.putUTF("ConstantValue");
constvalcontentIndex =
gcp.copyConstant(clazz.info.getConstantPool(), index);
}
}
public void write(DataOutputStream out) throws IOException {
out.writeShort(info.getModifiers());
out.writeShort(nameIndex);
out.writeShort(descriptorIndex);
if (constvalIndex != 0) {
out.writeShort(1); // number of Attributes
out.writeShort(constvalIndex);
out.writeInt(2); // length of Attribute
out.writeShort(constvalcontentIndex);
} else
out.writeShort(0);
} }
} }

@ -70,6 +70,8 @@ public abstract class Identifier {
* You shouldn't call this directly, but use setReachable instead. * You shouldn't call this directly, but use setReachable instead.
*/ */
protected void setSingleReachable() { protected void setSingleReachable() {
if (getParent() != null)
getParent().setReachable();
} }
/** /**
@ -178,17 +180,17 @@ public abstract class Identifier {
} }
newAlias.setCharAt(pos, '0'); newAlias.setCharAt(pos, '0');
} else { } else {
while (c++ < Character.MAX_VALUE) { while (c++ < 255) {
if (Character.isUnicodeIdentifierPart(c)) { if (Character.isJavaIdentifierPart(c)) {
newAlias.setCharAt(pos, c); newAlias.setCharAt(pos, c);
break okay; break okay;
} }
} }
newAlias.setCharAt(pos, '!'); newAlias.setCharAt(pos, '0');
} }
} }
newAlias.insert(0, renameRule == Obfuscator.RENAME_WEAK newAlias.insert(0, renameRule == Obfuscator.RENAME_WEAK
? 'A': '!'); ? 'A': '0');
} while (false); } while (false);
Identifier ptr = this; Identifier ptr = this;
while (ptr != null) { while (ptr != null) {
@ -208,6 +210,7 @@ public abstract class Identifier {
out.println("" + getFullAlias() + " = " + getName()); out.println("" + getFullAlias() + " = " + getName());
} }
public abstract Identifier getParent();
public abstract String getName(); public abstract String getName();
public abstract String getType(); public abstract String getType();
public abstract String getFullName(); public abstract String getFullName();

@ -23,9 +23,14 @@ import java.io.*;
import java.util.Vector; import java.util.Vector;
import java.util.Enumeration; import java.util.Enumeration;
public class MethodIdentifier extends Identifier { public class MethodIdentifier extends Identifier implements Opcodes {
ClassIdentifier clazz; ClassIdentifier clazz;
MethodInfo info; MethodInfo info;
CodeInfo codeinfo;
/**
* The exceptions that can be thrown by this method
*/
String[] exceptions;
public MethodIdentifier(ClassIdentifier clazz, MethodInfo info) { public MethodIdentifier(ClassIdentifier clazz, MethodInfo info) {
super(info.getName()); super(info.getName());
@ -33,53 +38,93 @@ public class MethodIdentifier extends Identifier {
this.info = info; this.info = info;
} }
public Vector analyzeCode(CodeInfo codeinfo) { /**
Vector references = new Vector(); * Reads the opcodes out of the code info and determine its
* references
* @return an enumeration of the references.
*/
public void analyzeCode() throws IOException{
ConstantPool cp = clazz.info.getConstantPool();
byte[] code = codeinfo.getCode(); byte[] code = codeinfo.getCode();
try {
DataInputStream stream = DataInputStream stream =
new DataInputStream(new ByteArrayInputStream(code)); new DataInputStream(new ByteArrayInputStream(code));
Opcodes.getReferences(stream, references); int addr = 0;
} catch (IOException ex) { while (stream.available() > 0) {
ex.printStackTrace(); int opcode = stream.readUnsignedByte();
throw new ClassFormatError(ex.getMessage()); switch (opcode) {
case opc_wide: {
switch (opcode = stream.readUnsignedByte()) {
case opc_iload: case opc_lload:
case opc_fload: case opc_dload: case opc_aload:
case opc_istore: case opc_lstore:
case opc_fstore: case opc_dstore: case opc_astore:
case opc_ret:
stream.skip(2);
addr+=4;
break;
case opc_iinc:
stream.skip(4);
addr+=6;
break;
default:
throw new ClassFormatError("Invalid wide opcode "+opcode);
} }
return references; }
case opc_ret:
stream.skip(1);
addr+=2;
break;
case opc_sipush:
case opc_ldc_w:
case opc_ldc2_w:
case opc_iinc:
case opc_ifnull: case opc_ifnonnull:
case opc_putstatic:
case opc_putfield:
stream.skip(2);
addr+=3;
break;
case opc_jsr_w:
case opc_goto_w:
stream.skip(4);
addr+=5;
break;
case opc_tableswitch: {
int length = 7-(addr % 4);
stream.skip(length);
int low = stream.readInt();
int high = stream.readInt();
stream.skip(4*(high-low+1));
addr += 9 + length + 4*(high-low+1);
break;
}
case opc_lookupswitch: {
int length = 7-(addr % 4);
stream.skip(length);
int npairs = stream.readInt();
stream.skip(8*npairs);
addr += 5 + length + 8*npairs;
break;
} }
public void setSingleReachable() { case opc_getstatic:
super.setSingleReachable(); case opc_getfield: {
int ref = stream.readUnsignedShort();
if (Obfuscator.isDebugging) String[] names = cp.getRef(ref);
Obfuscator.err.println("Reachable: "+this);
ConstantPool cp = clazz.info.getConstantPool();
AttributeInfo codeattr = info.findAttribute("Code");
if (codeattr == null)
return;
DataInputStream stream = new DataInputStream
(new ByteArrayInputStream(codeattr.getContents()));
CodeInfo codeinfo = new CodeInfo();
try {
codeinfo.read(clazz.info.getConstantPool(), stream);
Vector references = analyzeCode(codeinfo);
Enumeration enum = references.elements();
while (enum.hasMoreElements()) {
int[] ref = (int[]) enum.nextElement();
int tag = cp.getTag(ref[0]);
switch (tag) {
case ConstantPool.FIELDREF:
case ConstantPool.METHODREF:
case ConstantPool.INTERFACEMETHODREF: {
String[] names = cp.getRef(ref[0]);
clazz.bundle.reachableIdentifier clazz.bundle.reachableIdentifier
(names[0].replace('/','.')+"."+names[1]+"."+names[2], (names[0].replace('/','.')+"."+names[1]+"."+names[2],
ref[1] == 1); false);
addr += 3;
break; break;
} }
case ConstantPool.CLASS: { case opc_new:
String clName = cp.getClassName(ref[0]).replace('/','.'); case opc_anewarray:
case opc_checkcast:
case opc_instanceof:
case opc_multianewarray: {
int ref = stream.readUnsignedShort();
String clName = cp.getClassName(ref).replace('/','.');
if (clName.charAt(0) == '[') { if (clName.charAt(0) == '[') {
int i; int i;
for (i=0; i< clName.length(); i++) for (i=0; i< clName.length(); i++)
@ -93,13 +138,94 @@ public class MethodIdentifier extends Identifier {
clName = clName.substring(i+1, index); clName = clName.substring(i+1, index);
} }
clazz.bundle.reachableIdentifier(clName, false); clazz.bundle.reachableIdentifier(clName, false);
addr += 3;
if (opcode == opc_multianewarray) {
stream.skip(1);
addr ++;
}
break; break;
} }
case opc_invokespecial:
case opc_invokestatic:
case opc_invokeinterface:
case opc_invokevirtual: {
int ref = stream.readUnsignedShort();
String[] names = cp.getRef(ref);
clazz.bundle.reachableIdentifier
(names[0].replace('/','.')+"."+names[1]+"."+names[2],
opcode == opc_invokevirtual
|| opcode == opc_invokeinterface);
addr += 3;
if (opcode == opc_invokeinterface) {
stream.skip(2);
addr += 2;
}
break;
}
default: default:
throw new jode.AssertError if (opcode == opc_newarray
("Unknown reference: "+ref+" tag: "+tag); || (opcode >= opc_bipush && opcode <= opc_aload)
|| (opcode >= opc_istore && opcode <= opc_astore)) {
stream.skip(1);
addr += 2;
} else if (opcode >= opc_ifeq && opcode <= opc_jsr) {
stream.skip(2);
addr += 3;
} else if (opcode == opc_xxxunusedxxx
|| opcode >= opc_breakpoint)
throw new ClassFormatError("Invalid opcode "+opcode);
else
addr++;
} }
} }
}
public void readExceptions(AttributeInfo exceptionsattr)
throws IOException {
byte[] content = exceptionsattr.getContents();
DataInputStream input = new DataInputStream
(new ByteArrayInputStream(content));
ConstantPool cp = clazz.info.getConstantPool();
int count = input.readUnsignedShort();
exceptions = new String[count];
for (int i=0; i< count; i++) {
exceptions[i]
= cp.getClassName(input.readUnsignedShort()).replace('/','.');
clazz.bundle.reachableIdentifier(exceptions[i], false);
}
}
public void setSingleReachable() {
super.setSingleReachable();
if (Obfuscator.isDebugging)
Obfuscator.err.println("Reachable: "+this);
String type = getType();
int index = type.indexOf('L');
while (index != -1) {
int end = type.indexOf(';', index);
clazz.bundle.reachableIdentifier(type.substring(index+1, end)
, false);
index = type.indexOf('L', end);
}
AttributeInfo codeattr = info.findAttribute("Code");
AttributeInfo exceptionsattr = info.findAttribute("Exceptions");
try {
if (codeattr != null) {
DataInputStream stream = new DataInputStream
(new ByteArrayInputStream(codeattr.getContents()));
codeinfo = new CodeInfo();
codeinfo.read(clazz.info.getConstantPool(), stream);
analyzeCode();
}
if (exceptionsattr != null)
readExceptions(exceptionsattr);
} catch (IOException ex) { } catch (IOException ex) {
ex.printStackTrace(); ex.printStackTrace();
} }
@ -112,6 +238,10 @@ public class MethodIdentifier extends Identifier {
+ " = " + getName()); + " = " + getName());
} }
public Identifier getParent() {
return clazz;
}
public String getFullName() { public String getFullName() {
return clazz.getFullName() + "." + getName(); return clazz.getFullName() + "." + getName();
} }
@ -131,11 +261,419 @@ public class MethodIdentifier extends Identifier {
public boolean conflicting(String newAlias) { public boolean conflicting(String newAlias) {
String type = getType(); String type = getType();
String paramType = type.substring(0, type.indexOf(')')+1); String paramType = type.substring(0, type.indexOf(')')+1);
return clazz.containsMethod(newAlias, paramType) return clazz.getMethod(newAlias, paramType) != null
|| clazz.containsField(newAlias); || clazz.containFieldAlias(newAlias, "");
} }
public String toString() { public String toString() {
return "MethodIdentifier "+getFullName()+"."+getType(); return "MethodIdentifier "+getFullName()+"."+getType();
} }
int nameIndex;
int descriptorIndex;
int codeIndex;
byte[] code;
int exceptionsIndex;
int[] excIndices;
static byte[] buff = new byte[10];
static void copy(DataInputStream in, DataOutputStream out, int length)
throws IOException {
if (buff.length < length)
buff = new byte[length];
in.readFully(buff, 0, length);
out.write(buff, 0, length);
}
public void transformCode(GrowableConstantPool gcp) {
ConstantPool cp = clazz.info.getConstantPool();
byte[] origcode = codeinfo.getCode();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream output = new DataOutputStream(baos);
DataInputStream input = new DataInputStream
(new ByteArrayInputStream(origcode));
try {
output.writeShort(codeinfo.getMaxStack());
output.writeShort(codeinfo.getMaxLocals());
output.writeInt(origcode.length);
int addr = 0;
while (input.available() > 0) {
int opcode = input.readUnsignedByte();
switch (opcode) {
case opc_wide: {
output.writeByte(opcode);
int wideopcode = input.readUnsignedByte();
switch (wideopcode) {
case opc_iload: case opc_lload:
case opc_fload: case opc_dload: case opc_aload:
case opc_istore: case opc_lstore:
case opc_fstore: case opc_dstore: case opc_astore:
case opc_ret:
output.writeByte(wideopcode);
copy(input, output, 2);
addr+=4;
break;
case opc_iinc:
output.writeByte(wideopcode);
copy(input, output, 4);
addr+=6;
break;
default:
throw new ClassFormatError("Invalid wide opcode "
+wideopcode);
}
break;
}
case opc_ret:
output.writeByte(opcode);
copy(input, output, 1);
addr+=2;
break;
case opc_ldc: {
int index = input.readUnsignedByte();
int newIndex = gcp.copyConstant(cp, index);
output.writeByte(opcode);
output.writeByte(newIndex);
addr+=2;
break;
}
case opc_ldc_w:
case opc_ldc2_w: {
int index = input.readUnsignedShort();
int newIndex = gcp.copyConstant(cp, index);
output.writeByte(opcode);
output.writeShort(newIndex);
addr += 3;
break;
}
case opc_sipush:
case opc_iinc:
case opc_ifnull: case opc_ifnonnull:
output.writeByte(opcode);
copy(input, output, 2);
addr+=3;
break;
case opc_jsr_w:
case opc_goto_w:
output.writeByte(opcode);
copy(input, output, 4);
addr+=5;
break;
case opc_tableswitch: {
output.writeByte(opcode);
int length = 7-(addr % 4);
copy(input, output, length);
int low = input.readInt();
output.writeInt(low);
int high = input.readInt();
output.writeInt(high);
copy(input, output, 4*(high-low+1));
addr += 9 + length + 4*(high-low+1);
break;
}
case opc_lookupswitch: {
output.writeByte(opcode);
int length = 7-(addr % 4);
copy(input, output, length);
int npairs = input.readInt();
output.writeInt(npairs);
copy(input, output, 8*npairs);
addr += 5 + length + 8*npairs;
break;
}
case opc_getstatic:
case opc_getfield:
case opc_putstatic:
case opc_putfield: {
int ref = input.readUnsignedShort();
String[] names = cp.getRef(ref);
ClassIdentifier ci = (ClassIdentifier)
clazz.bundle.getIdentifier(names[0].replace('/','.'));
if (ci != null) {
names[0] = ci.getFullAlias();
Identifier fi = ci.getIdentifier(names[1], names[2]);
if (fi instanceof FieldIdentifier) {
if (!((FieldIdentifier)fi).isReachable()) {
if (opcode != opc_putfield
&& opcode != opc_putstatic)
throw new jode.AssertError
("reading not reachable field");
int stacksize =
(opcode == opc_putstatic) ? 0 : 1;
stacksize += jode.Type.tType
(names[2]).stackSize();
if (stacksize == 3) {
output.writeByte(opc_pop2);
output.writeByte(opc_pop);
output.writeByte(opc_nop);
} else if (stacksize == 2) {
output.writeByte(opc_pop2);
output.writeByte(opc_nop);
output.writeByte(opc_nop);
} else {
output.writeByte(opc_pop);
output.writeByte(opc_nop);
output.writeByte(opc_nop);
}
addr += 3;
break;
}
names[1] = ((FieldIdentifier) fi).getAlias();
}
}
names[2] = clazz.bundle.getTypeAlias(names[2]);
output.writeByte(opcode);
output.writeShort(gcp.putRef(cp.getTag(ref), names));
addr += 3;
break;
}
case opc_new:
case opc_anewarray:
case opc_checkcast:
case opc_instanceof:
case opc_multianewarray: {
int ref = input.readUnsignedShort();
String clName = cp.getClassName(ref).replace('/','.');
if (clName.charAt(0) == '[') {
clName = clazz.bundle.getTypeAlias(clName);
} else {
ClassIdentifier ci = (ClassIdentifier)
clazz.bundle.getIdentifier(clName);
if (ci != null)
clName = ci.getFullAlias();
}
int newRef = gcp.putClassRef(clName);
output.writeByte(opcode);
output.writeShort(newRef);
addr += 3;
if (opcode == opc_multianewarray) {
copy(input, output, 1);
addr ++;
}
break;
}
case opc_invokespecial:
case opc_invokestatic:
case opc_invokeinterface:
case opc_invokevirtual: {
int ref = input.readUnsignedShort();
String[] names = cp.getRef(ref);
ClassIdentifier ci = (ClassIdentifier)
clazz.bundle.getIdentifier(names[0].replace('/','.'));
if (ci != null) {
names[0] = ci.getFullAlias();
Identifier mi = ci.getIdentifier(names[1], names[2]);
if (mi instanceof MethodIdentifier) {
names[1] = ((MethodIdentifier)mi).getAlias();
}
}
names[2] = clazz.bundle.getTypeAlias(names[2]);
output.writeByte(opcode);
output.writeShort(gcp.putRef(cp.getTag(ref), names));
addr += 3;
if (opcode == opc_invokeinterface) {
copy(input, output, 2);
addr += 2;
}
break;
}
default:
output.writeByte(opcode);
if (opcode == opc_newarray
|| (opcode >= opc_bipush && opcode <= opc_aload)
|| (opcode >= opc_istore && opcode <= opc_astore)) {
copy(input, output, 1);
addr += 2;
} else if (opcode >= opc_ifeq && opcode <= opc_jsr) {
copy(input, output, 2);
addr += 3;
} else if (opcode == opc_xxxunusedxxx
|| opcode >= opc_breakpoint)
throw new ClassFormatError("Invalid opcode "+opcode);
else
addr++;
}
} }
int[] handlers = codeinfo.getExceptionHandlers();
output.writeShort(handlers.length / 4);
for (int i=0; i< handlers.length; i += 4) {
output.writeShort(handlers[i]);
output.writeShort(handlers[i+1]);
output.writeShort(handlers[i+2]);
String clName
= cp.getClassName(handlers[i+3]).replace('/','.');
ClassIdentifier ci = (ClassIdentifier)
clazz.bundle.getIdentifier(clName);
if (ci != null)
clName = ci.getFullAlias();
output.writeShort(gcp.putClassRef(clName));
}
output.writeShort(0); // No Attributes;
output.close();
} catch (IOException ex) {
ex.printStackTrace();
code = null;
return;
}
code = baos.toByteArray();
}
public void reserveSmallConstants(GrowableConstantPool gcp) {
if (codeinfo != null) {
ConstantPool cp = clazz.info.getConstantPool();
byte[] code = codeinfo.getCode();
DataInputStream stream =
new DataInputStream(new ByteArrayInputStream(code));
try {
int addr = 0;
while (stream.available() > 0) {
int opcode = stream.readUnsignedByte();
switch (opcode) {
case opc_ldc: {
int index = stream.readUnsignedByte();
gcp.copyConstant(cp, index);
break;
}
case opc_wide: {
switch (opcode = stream.readUnsignedByte()) {
case opc_iload: case opc_lload:
case opc_fload: case opc_dload: case opc_aload:
case opc_istore: case opc_lstore:
case opc_fstore: case opc_dstore: case opc_astore:
case opc_ret:
stream.skip(2);
addr+=4;
break;
case opc_iinc:
stream.skip(4);
addr+=6;
break;
default:
throw new ClassFormatError("Invalid wide opcode "+opcode);
}
}
case opc_tableswitch: {
int length = 7-(addr % 4);
stream.skip(length);
int low = stream.readInt();
int high = stream.readInt();
stream.skip(4*(high-low+1));
addr += 9 + length + 4*(high-low+1);
break;
}
case opc_lookupswitch: {
int length = 7-(addr % 4);
stream.skip(length);
int npairs = stream.readInt();
stream.skip(8*npairs);
addr += 5 + length + 8*npairs;
break;
}
case opc_ret:
stream.skip(1);
addr+=2;
break;
case opc_sipush:
case opc_ldc2_w:
case opc_iinc:
case opc_ifnull: case opc_ifnonnull:
case opc_new:
case opc_anewarray:
case opc_checkcast:
case opc_instanceof:
stream.skip(2);
addr+=3;
break;
case opc_multianewarray:
stream.skip(3);
addr += 4;
break;
case opc_jsr_w:
case opc_goto_w:
case opc_invokeinterface:
stream.skip(4);
addr+=5;
break;
default:
if (opcode == opc_newarray
|| (opcode >= opc_bipush && opcode <= opc_aload)
|| (opcode >= opc_istore && opcode <= opc_astore)) {
stream.skip(1);
addr += 2;
} else if (opcode >= opc_ifeq && opcode <= opc_jsr
|| opcode >= opc_getstatic && opcode <= opc_invokestatic) {
stream.skip(2);
addr += 3;
} else if (opcode == opc_xxxunusedxxx
|| opcode >= opc_breakpoint)
throw new ClassFormatError("Invalid opcode "+opcode);
else
addr++;
}
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
public void fillConstantPool(GrowableConstantPool gcp) {
nameIndex = gcp.putUTF(getAlias());
descriptorIndex = gcp.putUTF(clazz.bundle.getTypeAlias(getType()));
AttributeInfo codeattr = info.findAttribute("Code");
codeIndex = 0;
if (codeinfo != null) {
transformCode(gcp);
if (code != null)
codeIndex = gcp.putUTF("Code");
}
if (exceptions != null) {
exceptionsIndex = gcp.putUTF("Exceptions");
excIndices = new int[exceptions.length];
for (int i=0; i< exceptions.length; i++) {
ClassIdentifier ci = (ClassIdentifier)
clazz.bundle.getIdentifier(exceptions[i]);
if (ci != null)
excIndices[i] = gcp.putClassRef(ci.getFullAlias());
else
excIndices[i] = gcp.putClassRef(exceptions[i]);
}
}
}
public void write(DataOutputStream out) throws IOException {
out.writeShort(info.getModifiers());
out.writeShort(nameIndex);
out.writeShort(descriptorIndex);
int attrCount = 0;
if (code != null)
attrCount++;
if (excIndices != null)
attrCount++;
out.writeShort(attrCount);
if (code != null) {
out.writeShort(codeIndex);
out.writeInt(code.length);
out.write(code);
}
if (excIndices != null) {
out.writeShort(exceptionsIndex);
out.writeInt(excIndices.length*2+2);
out.writeShort(excIndices.length);
for (int i=0; i< excIndices.length; i++)
out.writeShort(excIndices[i]);
}
}
}

@ -31,6 +31,7 @@ public class PackageIdentifier extends Identifier {
PackageIdentifier parent; PackageIdentifier parent;
String name; String name;
boolean willStrip = false;
boolean loadOnDemand; boolean loadOnDemand;
Hashtable loadedClasses; Hashtable loadedClasses;
@ -70,14 +71,16 @@ public class PackageIdentifier extends Identifier {
String fullname = getFullName() + name; String fullname = getFullName() + name;
if (ClassInfo.isPackage(fullname)) { if (ClassInfo.isPackage(fullname)) {
ident = new PackageIdentifier(bundle, this, name, true); ident = new PackageIdentifier(bundle, this, name, true);
loadedClasses.put(name, ident);
} else if (!ClassInfo.exists(fullname)) { } else if (!ClassInfo.exists(fullname)) {
throw new IllegalArgumentException throw new IllegalArgumentException
("Can't find class"+fullname); ("Can't find class"+fullname);
} else { } else {
ident = new ClassIdentifier(bundle, this, name, ident = new ClassIdentifier(bundle, this, name,
ClassInfo.forName(fullname)); ClassInfo.forName(fullname));
}
loadedClasses.put(name, ident); loadedClasses.put(name, ident);
((ClassIdentifier) ident).initClass();
}
} }
return ident; return ident;
} else { } else {
@ -121,7 +124,6 @@ public class PackageIdentifier extends Identifier {
if (ident == null) if (ident == null)
return; return;
ident.setReachable();
if (index == -1) if (index == -1)
return; return;
@ -210,12 +212,11 @@ public class PackageIdentifier extends Identifier {
} }
public void strip() { public void strip() {
Hashtable ht = new Hashtable(); willStrip = true;
Enumeration enum = loadedClasses.elements(); Enumeration enum = loadedClasses.elements();
while (enum.hasMoreElements()) { while (enum.hasMoreElements()) {
Identifier ident = (Identifier) enum.nextElement(); Identifier ident = (Identifier) enum.nextElement();
if (ident.isReachable()) { if (ident.isReachable()) {
ht.put(ident.getName(), ident);
if (ident instanceof ClassIdentifier) { if (ident instanceof ClassIdentifier) {
((ClassIdentifier) ident).strip(); ((ClassIdentifier) ident).strip();
} else } else
@ -227,7 +228,6 @@ public class PackageIdentifier extends Identifier {
+ " is not reachable"); + " is not reachable");
} }
} }
loadedClasses = ht;
} }
public void buildTable(int renameRule) { public void buildTable(int renameRule) {
@ -248,8 +248,14 @@ public class PackageIdentifier extends Identifier {
+ " = " + getName()); + " = " + getName());
Enumeration enum = loadedClasses.elements(); Enumeration enum = loadedClasses.elements();
while (enum.hasMoreElements()) { while (enum.hasMoreElements()) {
((Identifier)enum.nextElement()).writeTable(out); Identifier ident = (Identifier) enum.nextElement();
if (!willStrip || ident.isReachable())
ident.writeTable(out);
}
} }
public Identifier getParent() {
return parent;
} }
public String getName() { public String getName() {
@ -262,16 +268,30 @@ public class PackageIdentifier extends Identifier {
public void storeClasses(File destination) { public void storeClasses(File destination) {
File newDest = (parent == null) ? destination File newDest = (parent == null) ? destination
: new File(destination, getName()); : new File(destination, getAlias());
if (!newDest.exists() && !newDest.mkdir()) {
Obfuscator.err.println("Could not create directory "
+newDest.getPath()+", check permissions.");
}
Enumeration enum = loadedClasses.elements(); Enumeration enum = loadedClasses.elements();
while (enum.hasMoreElements()) { while (enum.hasMoreElements()) {
Identifier ident = (Identifier) enum.nextElement(); Identifier ident = (Identifier) enum.nextElement();
if (willStrip && !ident.isReachable())
continue;
if (ident instanceof PackageIdentifier) if (ident instanceof PackageIdentifier)
((PackageIdentifier) ident) ((PackageIdentifier) ident)
.storeClasses(newDest); .storeClasses(newDest);
else { else {
try { try {
((ClassIdentifier) ident).storeClass(null); File file = new File(newDest, ident.getAlias()+".class");
if (file.exists()) {
Obfuscator.err.println
("Refuse to overwrite existing class file "
+file.getPath()+". Remove it first.");
}
DataOutputStream out = new DataOutputStream
(new FileOutputStream(file));
((ClassIdentifier) ident).storeClass(out);
} catch (java.io.IOException ex) { } catch (java.io.IOException ex) {
Obfuscator.err.println("Can't write Class " Obfuscator.err.println("Can't write Class "
+ ident.getName()); + ident.getName());

Loading…
Cancel
Save