*** 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. 311
      jode/jode/obfuscator/ClassIdentifier.java
  5. 59
      jode/jode/obfuscator/FieldIdentifier.java
  6. 11
      jode/jode/obfuscator/Identifier.java
  7. 652
      jode/jode/obfuscator/MethodIdentifier.java
  8. 36
      jode/jode/obfuscator/PackageIdentifier.java

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

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

@ -90,6 +90,11 @@ public class ClassBundle {
}
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));
}
}

@ -19,9 +19,7 @@
package jode.obfuscator;
import jode.Obfuscator;
import jode.bytecode.ClassInfo;
import jode.bytecode.FieldInfo;
import jode.bytecode.MethodInfo;
import jode.bytecode.*;
import java.lang.reflect.Modifier;
import java.util.*;
import java.io.*;
@ -31,8 +29,13 @@ public class ClassIdentifier extends Identifier {
PackageIdentifier pack;
String name;
ClassInfo info;
boolean willStrip;
int preserveRule;
int fieldCount;
/* The first fieldCount are of type FieldIdentifier, the remaining
* are MethodIdentifier
*/
Identifier[] identifiers;
Vector knownSubClasses = new Vector();
Vector virtualReachables = new Vector();
@ -65,7 +68,7 @@ public class ClassIdentifier extends Identifier {
for (int i=0; i< identifiers.length; i++) {
if (identifiers[i].getName().equals(name)
&& (typeSig == null
|| identifiers[i] .getType().equals(typeSig)))
|| identifiers[i].getType().equals(typeSig)))
identifiers[i].setPreserved();
}
}
@ -115,21 +118,20 @@ public class ClassIdentifier extends Identifier {
ClassIdentifier superident = (ClassIdentifier)
bundle.getIdentifier(superclass.getName());
if (superident != null) {
for (int i=0; i < superident.identifiers.length; i++)
if (superident.identifiers[i]
instanceof MethodIdentifier) {
MethodIdentifier mid = (MethodIdentifier)
superident.identifiers[i];
// all virtual methods in superclass must be chained.
int modif = mid.info.getModifiers();
if (((Modifier.PRIVATE
| Modifier.STATIC
| Modifier.FINAL) & modif) == 0
&& !(mid.getName().equals("<init>"))) {
// chain the preserved/same name lists.
chainIdentifier(superident.identifiers[i]);
}
for (int i=superident.fieldCount;
i < superident.identifiers.length; i++) {
MethodIdentifier mid = (MethodIdentifier)
superident.identifiers[i];
// all virtual methods in superclass must be chained.
int modif = mid.info.getModifiers();
if (((Modifier.PRIVATE
| Modifier.STATIC
| Modifier.FINAL) & modif) == 0
&& !(mid.getName().equals("<init>"))) {
// chain the preserved/same name lists.
chainIdentifier(superident.identifiers[i]);
}
}
} else {
// all methods and fields in superclass are preserved!
MethodInfo[] topmethods = superclass.getMethods();
@ -159,15 +161,20 @@ public class ClassIdentifier extends Identifier {
}
public void setSingleReachable() {
info.loadInfo(info.FULLINFO);
super.setSingleReachable();
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();
MethodInfo[] minfos = info.getMethods();
fieldCount = finfos.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]);
if ((preserveRule & (finfos[i].getModifiers() ^ Modifier.PRIVATE))
!= 0) {
@ -176,7 +183,7 @@ public class ClassIdentifier extends Identifier {
}
}
for (int i=0; i< minfos.length; i++) {
identifiers[finfos.length + i]
identifiers[fieldCount + i]
= new MethodIdentifier(this, minfos[i]);
if ((preserveRule & (minfos[i].getModifiers() ^ Modifier.PRIVATE))
!= 0) {
@ -192,7 +199,6 @@ public class ClassIdentifier extends Identifier {
ClassIdentifier ifaceident = (ClassIdentifier)
bundle.getIdentifier(ifaces[i].getName());
if (ifaceident != null) {
ifaceident.setReachable();
ifaceident.addSubClass(this);
}
initSuperClasses(ifaces[i]);
@ -202,49 +208,152 @@ public class ClassIdentifier extends Identifier {
ClassIdentifier superident = (ClassIdentifier)
bundle.getIdentifier(info.getSuperclass().getName());
if (superident != null) {
superident.setReachable();
superident.addSubClass(this);
}
initSuperClasses(info.getSuperclass());
}
preserveIdentifier("<clinit>", "()V");
preserveIdentifier("<init>", null);
}
public void strip() {
int newLength = 0;
for (int i=0; i < identifiers.length; i++) {
if (identifiers[i].isReachable())
newLength++;
else {
if (Obfuscator.isDebugging)
willStrip = true;
if (Obfuscator.isDebugging) {
for (int i=0; i < identifiers.length; i++) {
if (!identifiers[i].isReachable())
Obfuscator.err.println(identifiers[i].toString()
+ " 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) {
super.buildTable(renameRule);
for (int i=0; i < identifiers.length; i++)
identifiers[i].buildTable(renameRule);
if (!willStrip || identifiers[i].isReachable())
identifiers[i].buildTable(renameRule);
}
public void writeTable(PrintWriter out) throws IOException {
if (getName() != getAlias())
out.println("" + getFullAlias() + " = " + getName());
for (int i=0; i < identifiers.length; i++)
identifiers[i].writeTable(out);
if (!willStrip || identifiers[i].isReachable())
identifiers[i].writeTable(out);
}
public void storeClass(OutputStream out) throws IOException {
/*XXX*/
public void addIfaces(Vector result, ClassInfo[] ifaces) {
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,42 +382,78 @@ public class ClassIdentifier extends Identifier {
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();
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;
}
ClassInfo[] ifaces = clazz.getInterfaces();
for (int i=0; i < ifaces.length; i++) {
if (containsField(ifaces[i], newAlias))
if (containFieldAlias(ifaces[i], fieldName, typeSig))
return true;
}
if (clazz.getSuperclass() != null) {
if (containsField(clazz.getSuperclass(), newAlias))
if (containFieldAlias(clazz.getSuperclass(),
fieldName, typeSig))
return true;
}
return false;
}
public boolean containsField(String newAlias) {
for (int i=0; i< identifiers.length; i++) {
if (identifiers[i] instanceof FieldIdentifier
&& identifiers[i].getAlias().equals(newAlias))
public boolean containFieldAlias(String fieldName, String typeSig) {
for (int i=0; i < fieldCount; i++) {
if ((!willStrip || identifiers[i].isReachable())
&& identifiers[i].getAlias().equals(fieldName)
&& identifiers[i].getType().startsWith(typeSig))
return true;
}
ClassInfo[] ifaces = info.getInterfaces();
for (int i=0; i < ifaces.length; i++) {
ClassIdentifier ifaceident = (ClassIdentifier)
bundle.getIdentifier(ifaces[i].getName());
if (ifaceident != null) {
if (ifaceident.containsField(newAlias))
if (ifaceident.containFieldAlias(fieldName, typeSig))
return true;
} else {
if (containsField(ifaces[i], newAlias))
if (containFieldAlias(ifaces[i], fieldName, typeSig))
return true;
}
}
@ -317,45 +462,49 @@ public class ClassIdentifier extends Identifier {
ClassIdentifier superident = (ClassIdentifier)
bundle.getIdentifier(info.getSuperclass().getName());
if (superident != null) {
if (superident.containsField(newAlias))
if (superident.containFieldAlias(fieldName, typeSig))
return true;
} else {
if (containsField(info.getSuperclass(), newAlias))
if (containFieldAlias(info.getSuperclass(),
fieldName, typeSig))
return true;
}
}
return false;
}
public static boolean containsMethod(ClassInfo clazz,
String newAlias, String paramType) {
public static Object getMethod(ClassInfo clazz,
String methodName, String paramType) {
MethodInfo[] minfos = clazz.getMethods();
for (int i=0; i< minfos.length; i++) {
if (minfos[i].getName().equals(newAlias)
if (minfos[i].getName().equals(methodName)
&& minfos[i].getType().getTypeSignature()
.startsWith(paramType))
return true;
return minfos[i];
}
ClassInfo[] ifaces = clazz.getInterfaces();
for (int i=0; i < ifaces.length; i++) {
if (containsMethod(ifaces[i], newAlias, paramType))
return true;
Object result = getMethod(ifaces[i], methodName, paramType);
if (result != null)
return result;
}
if (clazz.getSuperclass() != null) {
if (containsMethod(clazz.getSuperclass(), newAlias, paramType))
return true;
Object result = getMethod(clazz.getSuperclass(),
methodName, paramType);
if (result != null)
return result;
}
return false;
return null;
}
public boolean containsMethod(String newAlias, String paramType) {
for (int i=0; i< identifiers.length; i++) {
if (identifiers[i] instanceof MethodIdentifier
&& identifiers[i].getAlias().equals(newAlias)
public Object getMethod(String methodName, String paramType) {
for (int i=fieldCount; i< identifiers.length; i++) {
if ((!willStrip || identifiers[i].isReachable())
&& identifiers[i].getAlias().equals(methodName)
&& identifiers[i].getType().startsWith(paramType))
return true;
return identifiers[i];
}
ClassInfo[] ifaces = info.getInterfaces();
@ -363,11 +512,13 @@ public class ClassIdentifier extends Identifier {
ClassIdentifier ifaceident = (ClassIdentifier)
bundle.getIdentifier(ifaces[i].getName());
if (ifaceident != null) {
if (ifaceident.containsMethod(newAlias, paramType))
return true;
Object result = ifaceident.getMethod(methodName, paramType);
if (result != null)
return result;
} else {
if (containsMethod(ifaces[i], newAlias, paramType))
return true;
Object result = getMethod(ifaces[i], methodName, paramType);
if (result != null)
return result;
}
}
@ -375,17 +526,21 @@ public class ClassIdentifier extends Identifier {
ClassIdentifier superident = (ClassIdentifier)
bundle.getIdentifier(info.getSuperclass().getName());
if (superident != null) {
if (superident.containsMethod(newAlias, paramType))
return true;
Object result = superident.getMethod(methodName, paramType);
if (result != null)
return result;
} else {
if (containsMethod(info.getSuperclass(), newAlias, paramType))
return true;
Object result = getMethod(info.getSuperclass(),
methodName, paramType);
if (result != null)
return result;
}
}
return false;
return null;
}
public boolean conflicting(String newAlias) {
return pack.contains(newAlias);
}
}

@ -17,7 +17,8 @@
* $Id$
*/
package jode.obfuscator;
import jode.bytecode.FieldInfo;
import jode.bytecode.*;
import java.io.*;
public class FieldIdentifier extends Identifier{
FieldInfo info;
@ -29,6 +30,21 @@ public class FieldIdentifier extends Identifier{
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() {
return clazz.getFullName() + "." + getName();
}
@ -50,7 +66,44 @@ public class FieldIdentifier extends Identifier{
}
public boolean conflicting(String newAlias) {
return clazz.containsField(newAlias)
|| clazz.containsMethod(newAlias, "");
return clazz.containFieldAlias(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.
*/
protected void setSingleReachable() {
if (getParent() != null)
getParent().setReachable();
}
/**
@ -178,17 +180,17 @@ public abstract class Identifier {
}
newAlias.setCharAt(pos, '0');
} else {
while (c++ < Character.MAX_VALUE) {
if (Character.isUnicodeIdentifierPart(c)) {
while (c++ < 255) {
if (Character.isJavaIdentifierPart(c)) {
newAlias.setCharAt(pos, c);
break okay;
}
}
newAlias.setCharAt(pos, '!');
newAlias.setCharAt(pos, '0');
}
}
newAlias.insert(0, renameRule == Obfuscator.RENAME_WEAK
? 'A': '!');
? 'A': '0');
} while (false);
Identifier ptr = this;
while (ptr != null) {
@ -208,6 +210,7 @@ public abstract class Identifier {
out.println("" + getFullAlias() + " = " + getName());
}
public abstract Identifier getParent();
public abstract String getName();
public abstract String getType();
public abstract String getFullName();

@ -23,9 +23,14 @@ import java.io.*;
import java.util.Vector;
import java.util.Enumeration;
public class MethodIdentifier extends Identifier {
public class MethodIdentifier extends Identifier implements Opcodes {
ClassIdentifier clazz;
MethodInfo info;
CodeInfo codeinfo;
/**
* The exceptions that can be thrown by this method
*/
String[] exceptions;
public MethodIdentifier(ClassIdentifier clazz, MethodInfo info) {
super(info.getName());
@ -33,18 +38,164 @@ public class MethodIdentifier extends Identifier {
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();
try {
DataInputStream stream =
new DataInputStream(new ByteArrayInputStream(code));
Opcodes.getReferences(stream, references);
} catch (IOException ex) {
ex.printStackTrace();
throw new ClassFormatError(ex.getMessage());
}
return references;
DataInputStream stream =
new DataInputStream(new ByteArrayInputStream(code));
int addr = 0;
while (stream.available() > 0) {
int opcode = stream.readUnsignedByte();
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);
}
}
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;
}
case opc_getstatic:
case opc_getfield: {
int ref = stream.readUnsignedShort();
String[] names = cp.getRef(ref);
clazz.bundle.reachableIdentifier
(names[0].replace('/','.')+"."+names[1]+"."+names[2],
false);
addr += 3;
break;
}
case opc_new:
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) == '[') {
int i;
for (i=0; i< clName.length(); i++)
if (clName.charAt(i) != '[')
break;
if (i >= clName.length() || clName.charAt(i) != 'L')
break;
int index = clName.indexOf(';', i);
if (index != clName.length()-1)
break;
clName = clName.substring(i+1, index);
}
clazz.bundle.reachableIdentifier(clName, false);
addr += 3;
if (opcode == opc_multianewarray) {
stream.skip(1);
addr ++;
}
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:
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) {
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() {
@ -53,53 +204,28 @@ public class MethodIdentifier extends Identifier {
if (Obfuscator.isDebugging)
Obfuscator.err.println("Reachable: "+this);
ConstantPool cp = clazz.info.getConstantPool();
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");
if (codeattr == null)
return;
AttributeInfo exceptionsattr = info.findAttribute("Exceptions");
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
(names[0].replace('/','.')+"."+names[1]+"."+names[2],
ref[1] == 1);
break;
}
case ConstantPool.CLASS: {
String clName = cp.getClassName(ref[0]).replace('/','.');
if (clName.charAt(0) == '[') {
int i;
for (i=0; i< clName.length(); i++)
if (clName.charAt(i) != '[')
break;
if (i >= clName.length() || clName.charAt(i) != 'L')
break;
int index = clName.indexOf(';', i);
if (index != clName.length()-1)
break;
clName = clName.substring(i+1, index);
}
clazz.bundle.reachableIdentifier(clName, false);
break;
}
default:
throw new jode.AssertError
("Unknown reference: "+ref+" tag: "+tag);
}
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) {
ex.printStackTrace();
}
@ -112,6 +238,10 @@ public class MethodIdentifier extends Identifier {
+ " = " + getName());
}
public Identifier getParent() {
return clazz;
}
public String getFullName() {
return clazz.getFullName() + "." + getName();
}
@ -131,11 +261,419 @@ public class MethodIdentifier extends Identifier {
public boolean conflicting(String newAlias) {
String type = getType();
String paramType = type.substring(0, type.indexOf(')')+1);
return clazz.containsMethod(newAlias, paramType)
|| clazz.containsField(newAlias);
return clazz.getMethod(newAlias, paramType) != null
|| clazz.containFieldAlias(newAlias, "");
}
public String toString() {
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;
String name;
boolean willStrip = false;
boolean loadOnDemand;
Hashtable loadedClasses;
@ -70,14 +71,16 @@ public class PackageIdentifier extends Identifier {
String fullname = getFullName() + name;
if (ClassInfo.isPackage(fullname)) {
ident = new PackageIdentifier(bundle, this, name, true);
loadedClasses.put(name, ident);
} else if (!ClassInfo.exists(fullname)) {
throw new IllegalArgumentException
("Can't find class"+fullname);
} else {
ident = new ClassIdentifier(bundle, this, name,
ClassInfo.forName(fullname));
loadedClasses.put(name, ident);
((ClassIdentifier) ident).initClass();
}
loadedClasses.put(name, ident);
}
return ident;
} else {
@ -121,7 +124,6 @@ public class PackageIdentifier extends Identifier {
if (ident == null)
return;
ident.setReachable();
if (index == -1)
return;
@ -210,12 +212,11 @@ public class PackageIdentifier extends Identifier {
}
public void strip() {
Hashtable ht = new Hashtable();
willStrip = true;
Enumeration enum = loadedClasses.elements();
while (enum.hasMoreElements()) {
Identifier ident = (Identifier) enum.nextElement();
if (ident.isReachable()) {
ht.put(ident.getName(), ident);
if (ident instanceof ClassIdentifier) {
((ClassIdentifier) ident).strip();
} else
@ -227,7 +228,6 @@ public class PackageIdentifier extends Identifier {
+ " is not reachable");
}
}
loadedClasses = ht;
}
public void buildTable(int renameRule) {
@ -248,10 +248,16 @@ public class PackageIdentifier extends Identifier {
+ " = " + getName());
Enumeration enum = loadedClasses.elements();
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() {
return name;
}
@ -262,16 +268,30 @@ public class PackageIdentifier extends Identifier {
public void storeClasses(File 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();
while (enum.hasMoreElements()) {
Identifier ident = (Identifier) enum.nextElement();
if (willStrip && !ident.isReachable())
continue;
if (ident instanceof PackageIdentifier)
((PackageIdentifier) ident)
.storeClasses(newDest);
else {
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) {
Obfuscator.err.println("Can't write Class "
+ ident.getName());

Loading…
Cancel
Save