/**
* JavaGuard -- an obfuscation package for Java classfiles.
*
* Copyright (c) 1999 Mark Welsh (markw@retrologic.com)
* Copyright (c) 2002 Thorsten Heit (theit@gmx.de)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* The author may be contacted at theit@gmx.de.
*
*
* $Id: ClassFile.java,v 1.9 2002/06/04 09:56:47 glurk Exp $
*/
package net.sf.javaguard.classfile;
import java.io.*;
import java.util.*;
import net.sf.javaguard.Cl;
import net.sf.javaguard.KeyValue;
import net.sf.javaguard.ClassTree;
import net.sf.javaguard.log.FileLogger;
import net.sf.javaguard.Tools;
/** This is a representation of the data in a Java class file (*.class).
* A ClassFile instance representing a *.class file can be generated
* using the static create(DataInput) method, manipulated using various
* operators, and persisted back using the write(DataOutput) method.
*
* @author <a href="mailto:theit@gmx.de">Thorsten Heit</a>
* @author <a href="mailto:markw@retrologic.com">Mark Welsh</a>
*/
public class ClassFile implements ClassConstants {
// Constants -------------------------------------------------------------
private static final String[] DANGEROUS_CLASS_SIMPLENAME_DESCRIPTOR_ARRAY = {
"forName(Ljava/lang/String;)Ljava/lang/Class;",
"getDeclaredField(Ljava/lang/String;)Ljava/lang/reflect/Field;",
"getField(Ljava/lang/String;)Ljava/lang/reflect/Field;",
"getDeclaredMethod(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;",
"getMethod(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;"
};
private static final String LOG_DANGER_CLASS_PRE = " Your class ";
private static final String LOG_DANGER_CLASS_MID = " calls the java/lang/Class method ";
private static final String[] DANGEROUS_CLASSLOADER_SIMPLENAME_DESCRIPTOR_ARRAY = {
"defineClass(Ljava/lang/String;[BII)Ljava/lang/Class;",
"findLoadedClass(Ljava/lang/String;)Ljava/lang/Class;",
"findSystemClass(Ljava/lang/String;)Ljava/lang/Class;",
"loadClass(Ljava/lang/String;)Ljava/lang/Class;",
"loadClass(Ljava/lang/String;Z)Ljava/lang/Class;"
};
private static final String LOG_DANGER_CLASSLOADER_PRE = " Your class ";
private static final String LOG_DANGER_CLASSLOADER_MID = " calls the java/lang/ClassLoader method ";
/** Holds the class tree the class file is assigned to. */
private ClassTree classTree;
/** Holds the magic number stored in the class file (must be 0xcafebabe). */
private int magic;
/** Holds the minor version number. */
private int minorVersion;
/** Holds the major version number. */
private int majorVersion;
/** Holds the constant pool of the class file. */
private ConstantPool constantPool;
/** Holds the access flags for the class. */
private int accessFlags;
/** Index into the constant pool. The referenced element holds an index that
* references the class name. */
private int thisClassIndex;
/** Index into the constant pool. The referenced element holds an index that
* references the name of the super class. */
private int superClassIndex;
/** Holds the number of direct implemented interfaces. */
private int interfacesCount;
/** Index array of interface names in the constant pool. */
private int interfaces[];
/** Holds the number of direct declared fields. */
private int fieldsCount;
/** Holds information about the declared fields. */
private FieldInfo fieldInfo[];
/** Holds the number of declared methods. */
private int methodsCount;
/** Holds information about the declared methods. */
private MethodInfo methodInfo[];
/** Holds the number of attributes for the class file. */
private int attributesCount;
/** Holds information about the attributes for the class file. */
private AttrInfo attributesInfo[];
/** Create a new ClassFile from the class file format data in the DataInput
* stream.
* @param classTree the class tree the class file is assigned to
* @param din the input stream
* @return the class file created from the input stream
* @throws IOException if class file is corrupt or incomplete
*/
public static ClassFile create(ClassTree classTree, DataInput din)
throws IOException {
if (null == din) throw new IOException("No input stream was provided.");
ClassFile cf = new ClassFile(classTree);
cf.read(din);
return cf;
}
/** Private constructor.
* @param classTree the class tree the class file is assigned to
*/
private ClassFile(ClassTree classTree) {
this.classTree = classTree;
}
/** Returns the class tree the class file is assigned to.
* @return the class tree
*/
private ClassTree getClassTree() {
return classTree;
}
/** Import the class data to internal representation.
* @param din the input stream
* @throws IOException if an I/O error occurs
*/
private void read(DataInput din)
throws IOException {
// Read the class file
setMagic(din.readInt());
setMinorVersion(din.readUnsignedShort());
setMajorVersion(din.readUnsignedShort());
// Check this is a valid classfile that we can handle
if (getMagic() != MAGIC) {
throw new IOException("Invalid magic number in class file.");
}
if (getMajorVersion() > MAJOR_VERSION ||
(MAJOR_VERSION == getMajorVersion() && getMinorVersion() > MINOR_VERSION_MAX)) {
throw new IOException("Incompatible version number for class file format: "
+ getMajorVersion() + " / " + getMinorVersion());
}
int u2constantPoolCount = din.readUnsignedShort();
CpInfo[] cpInfo = new CpInfo[u2constantPoolCount];
// Fill the constant pool, recalling the zero entry
// is not persisted, nor are the entries following a Long or Double
for (int i = 1; i < u2constantPoolCount; i++) {
cpInfo[i] = CpInfo.create(din);
if ((cpInfo[i] instanceof LongCpInfo) ||
(cpInfo[i] instanceof DoubleCpInfo)) {
i++;
}
}
constantPool = new ConstantPool(this, cpInfo);
setAccessFlags(din.readUnsignedShort());
setClassIndex(din.readUnsignedShort());
setSuperClassIndex(din.readUnsignedShort());
// read the information about the implemented interfaces
setInterfacesCount(din.readUnsignedShort());
int[] u2interfaces = new int[getInterfacesCount()];
for (int i = 0; i < getInterfacesCount(); i++) {
u2interfaces[i] = din.readUnsignedShort();
}
setInterfaces(u2interfaces);
// read the direct declared fields
setFieldsCount(din.readUnsignedShort());
FieldInfo[] fields = new FieldInfo[getFieldsCount()];
for (int i = 0; i < getFieldsCount(); i++) {
fields[i] = FieldInfo.create(din, this);
}
setFieldInfo(fields);
// read the direct declared methods
setMethodsCount(din.readUnsignedShort());
MethodInfo[] methods = new MethodInfo[getMethodsCount()];
for (int i = 0; i < getMethodsCount(); i++) {
methods[i] = MethodInfo.create(din, this);
}
setMethodInfo(methods);
// read the attributes for the class
setAttributesCount(din.readUnsignedShort());
AttrInfo[] attrs = new AttrInfo[getAttributesCount()];
for (int i = 0; i < getAttributesCount(); i++) {
attrs[i] = AttrInfo.create(din, this);
}
setAttributeInfo(attrs);
}
/** Sets the magic number of the class file (normally 0xcafebabe).
* @param num the magic number
* @see #getMagic
*/
private void setMagic(int num) {
this.magic = num;
}
/** Returns the magic number of the class file (normally 0xcafebabe).
* @return magic number
* @see #setMagic
*/
private int getMagic() {
return magic;
}
/** Sets the major version of the class file.
* @param ver major class file version
* @see #getMajorVersion
*/
private void setMajorVersion(int ver) {
majorVersion = ver;
}
/** Returns the major version of the class file.
* @return major class file version
* @see #setMajorVersion
*/
private int getMajorVersion() {
return majorVersion;
}
/** Sets the minor version of the class file.
* @param ver minor class file version
* @see #setMinorVersion
*/
private void setMinorVersion(int ver) {
minorVersion = ver;
}
/** Returns the minor version of the class file.
* @return minor class file version
* @see #setMinorVersion
*/
private int getMinorVersion() {
return minorVersion;
}
/** Sets the access flags for the class.
* @param flags the access flags for the class
* @see #getAccessFlags
*/
private void setAccessFlags(int flags) {
accessFlags = flags;
}
/** Returns the access flags for the class.
* @return access flags for the class
* @see #setAccessFlags
*/
private int getAccessFlags() {
return accessFlags;
}
/** Sets the index for the element in the constant pool that knows the class
* name.
* @param index index into the constant pool
* @see #getClassIndex
*/
private void setClassIndex(int index) {
thisClassIndex = index;
}
/** Returns the index for the element in the constant pool that knows the
* class name.
* @return index into the constant pool
* @see #setClassIndex
*/
private int getClassIndex() {
return thisClassIndex;
}
/** Sets the index for the element in the constant pool that knows the name
* of the super class.
* @param index index into the constant pool
* @see #getSuperClassIndex
*/
private void setSuperClassIndex(int index) {
superClassIndex = index;
}
/** Returns the index for the element in the constant pool that knows the
* name of the super class.
* @return index into the constant pool
*/
private int getSuperClassIndex() {
return superClassIndex;
}
/** Sets the number of direct implemented interfaces.
* @param num the number of direct implemented interfaces
* @see #getInterfacesCount
*/
private void setInterfacesCount(int num) {
interfacesCount = num;
}
/** Returns the number of direct implemented interfaces.
* @return number of direct implemented interfaces
* @see #setInterfacesCount
*/
private int getInterfacesCount() {
return interfacesCount;
}
/** Sets the index array of interface names in the constant pool.
* @param interfaces index array of interface names in the constant pool
* @see #getInterfaces
*/
private void setInterfaces(int[] interfaces) {
this.interfaces = interfaces;
}
/** Returns the index array of interface names in the constant pool.
* @return index array of interface names in the constant pool
* @see #setInterfaces
* @see #getInterface
*/
private int[] getInterfaces() {
return interfaces;
}
/** Return the index of the specified interface from the index array.
* @param pos the position of the interface in the interface name index array
* @return index into the constant pool
* @see #getInterfaces
*/
private int getInterface(int pos) {
return interfaces[pos];
}
/** Returns an array with the names of the direct implemented interfaces.
* @return array with interface names
*/
public String[] getInterfaceNames() {
String[] names = new String[getInterfacesCount()];
for (int i=0; i<getInterfacesCount(); i++) {
names[i] = toName(getInterface(i));
}
return names;
}
/** Sets the number of declared fields.
* @param num number of declared fields
* @see #getFieldsCount
*/
private void setFieldsCount(int num) {
fieldsCount = num;
}
/** Returns the number of declared fields.
* @return number of declared fields
*/
public int getFieldsCount() {
return fieldsCount;
}
/** Sets the information about the declared fields.
* @param fields array with information about the declared fields
* @see #getFieldInfo
*/
private void setFieldInfo(FieldInfo[] fields) {
this.fieldInfo = fields;
}
/** Returns an array with information about the declared fields.
* @return array with information about the declared fields
* @see #setFieldInfo
*/
private FieldInfo[] getFieldInfo() {
return fieldInfo;
}
/** Return the information about a declared field.
* @param pos the position in the array of declared fields
* @return information about the declared fieldd
* @see #getFieldInfo
*/
public FieldInfo getFieldInfo(int pos) {
return fieldInfo[pos];
}
/** Sets the number of declared methods.
* @param num the number of declared methods
* @see #getMethodsCount
*/
private void setMethodsCount(int num) {
methodsCount = num;
}
/** Returns the number of declared methods.
* @return the number of declared methods
*/
public int getMethodsCount() {
return methodsCount;
}
/** Sets the information about the declared methods.
* @param methods array with information about the declared methods
* @see #getMethodInfo
*/
private void setMethodInfo(MethodInfo[] methods) {
this.methodInfo = methods;
}
/** Returns an array with information about the declared methods.
* @return array with information about the declared methods
* @see #setMethodInfo
*/
private MethodInfo[] getMethodInfo() {
return methodInfo;
}
/** Return the information about a declared method.
* @param pos the position in the array of declared methods
* @return information about the declared method
* @see #getMethodInfo
*/
public MethodInfo getMethodInfo(int pos) {
return methodInfo[pos];
}
/** Sets the number of attributes for the class file.
* @param num the number of attributes for the class file
* @see #getAttributesCount
*/
private void setAttributesCount(int num) {
attributesCount = num;
}
/** Returns the number of attributes for the class file.
* @return the number of attributes for the class file
* @see #setAttributesCount
*/
private int getAttributesCount() {
return attributesCount;
}
/** Sets the information about the attributes for the class file.
* @param attrs array with information about the attributes for the class file
* @see #getAttributeInfo
*/
private void setAttributeInfo(AttrInfo[] attrs) {
this.attributesInfo = attrs;
}
/** Returns an array with information about the attributes for the class file.
* @return array with information about the attributes for the class file
* @see #setAttributeInfo
*/
private AttrInfo[] getAttributeInfo() {
return attributesInfo;
}
/** Return the information about an attribute for the class file.
* @param pos the position in the array of attributes
* @return information about the attribute
* @see #getAttributeInfo
* @see #setAttributeInfo(int, AttrInfo)
*/
private AttrInfo getAttributeInfo(int pos) {
return attributesInfo[pos];
}
/** Sets the information about a certain attribute.
* @param pos the position in the array of attributes
* @param attrInfo the new attribute information
* @see #getAttributeInfo(int)
* @see #setAttributeInfo
*/
private void setAttributeInfo(int pos, AttrInfo attrInfo) {
attributesInfo[pos] = attrInfo;
}
/** Return the name of the class file.
* @return the name of the class file
*/
public String getName() {
return toName(getClassIndex());
}
/** Return the name of this class's super class.
* @return the name of the super class
*/
public String getSuper() {
// This may be java/lang/Object, in which case there is no super
return (0 == getSuperClassIndex()) ? null : toName(getSuperClassIndex());
}
/** Convert a constant pool index to a class name.
* @param index the index into the constant pool
* @return the name of the specified class
* @throws IllegalStateException if an error occurs
*/
protected String toName(int index)
throws IllegalStateException {
CpInfo classEntry = getCpEntry(index);
if (classEntry instanceof ClassCpInfo) {
CpInfo nameEntry = getCpEntry( ((ClassCpInfo) classEntry).getClassNameIndex());
if (nameEntry instanceof Utf8CpInfo) {
return ((Utf8CpInfo) nameEntry).getString();
}
}
throw new IllegalStateException("Inconsistent Constant Pool in class file.");
}
/** Lookup the entry in the constant pool and return the object.
* @param index the index into the constant pool
* @return the entry at the specified index in the constant pool
*/
protected CpInfo getCpEntry(int index) {
return constantPool.getCpEntry(index);
}
/** Check for methods which can break the obfuscated code, and log them to
* a string array.
* @return string array with methods which can break the obfuscated code
*/
public String[] dangerousMethods() {
Vector warningVec = new Vector();
// Need only check CONSTANT_Methodref entries of constant pool since
// dangerous methods belong to classes 'Class' and 'ClassLoader', not to interfaces.
for (Iterator iter = constantPool.iterator(); iter.hasNext(); ) {
Object obj = iter.next();
if (obj instanceof MethodrefCpInfo) {
// Get the method class name, simple name and descriptor
MethodrefCpInfo entry = (MethodrefCpInfo) obj;
ClassCpInfo classEntry = (ClassCpInfo) getCpEntry(entry.getClassIndex());
String className = ((Utf8CpInfo) getCpEntry(classEntry.getClassNameIndex())).getString();
NameAndTypeCpInfo ntEntry = (NameAndTypeCpInfo) getCpEntry(entry.getNameAndTypeIndex());
String name = ((Utf8CpInfo) getCpEntry(ntEntry.getNameIndex())).getString();
String descriptor = ((Utf8CpInfo) getCpEntry(ntEntry.getDescriptorIndex())).getString();
// Check if this is on the proscribed list
if (className.equals("java/lang/Class") &&
Tools.isInArray(name + descriptor, DANGEROUS_CLASS_SIMPLENAME_DESCRIPTOR_ARRAY)) {
warningVec.addElement(LOG_DANGER_CLASS_PRE + getName() + LOG_DANGER_CLASS_MID + name + descriptor);
}
else if (Tools.isInArray(name + descriptor, DANGEROUS_CLASSLOADER_SIMPLENAME_DESCRIPTOR_ARRAY)) {
warningVec.addElement(LOG_DANGER_CLASSLOADER_PRE + getName() + LOG_DANGER_CLASSLOADER_MID + name + descriptor);
}
}
}
// Copy any warnings to a String[]
String[] warnings = new String[warningVec.size()];
for (int i = 0; i < warnings.length; i++) {
warnings[i] = (String)warningVec.elementAt(i);
}
return warnings;
}
/** Check for methods which can break the obfuscated code, and log them. */
private static boolean hasHeader = false;
public static void resetDangerHeader() {
hasHeader = false;
}
public void logDangerousMethods() {
// Get any warnings and print them to the logfile
String[] warnings = dangerousMethods();
if (warnings != null && warnings.length > 0) {
FileLogger logfile = FileLogger.getInstance();
if (!hasHeader) {
logfile.addMethodWarning("#");
logfile.addMethodWarning("# WARNING - Methods are called which may unavoidably break in obfuscated version at runtime.");
logfile.addMethodWarning("# Please review your source code to ensure that the dangerous methods are not intended");
logfile.addMethodWarning("# to act on classes which are within the obfuscated Jar file.");
logfile.addMethodWarning("#");
hasHeader = true;
}
for (int i = 0; i < warnings.length; i++) {
logfile.addMethodWarning("# " + warnings[i]);
}
}
}
/** Check for direct references to Utf8 constant pool entries.
* @param pool the constant pool
* @throws IllegalStateException if a reference is invalid
*/
public void markUtf8Refs(ConstantPool pool)
throws IllegalStateException {
try {
// Check for references to Utf8 from outside the constant pool
for (int i = 0; i < getFieldsCount(); i++) {
getFieldInfo(i).markUtf8Refs(pool);
}
for (int i = 0; i < getMethodsCount(); i++) {
getMethodInfo(i).markUtf8Refs(pool); // also checks Code/LVT attrs here
}
for (int i = 0; i < getAttributesCount(); i++) {
getAttributeInfo(i).markUtf8Refs(pool); // checks InnerClasses, SourceFile and all attr names
}
// Now check for references from other CP entries
for (Iterator iter = pool.iterator(); iter.hasNext(); ) {
Object obj = iter.next();
if (obj instanceof NameAndTypeCpInfo ||
obj instanceof ClassCpInfo ||
obj instanceof StringCpInfo) {
((CpInfo) obj).markUtf8Refs(pool);
}
}
} catch (ArrayIndexOutOfBoundsException e) {
throw new IllegalStateException("Inconsistent reference to constant pool.");
}
}
/** Check for direct references to NameAndType constant pool entries.
* @param pool the constant pool
* @throws IllegalStateException if a reference is invalid
*/
public void markNTRefs(ConstantPool pool)
throws IllegalStateException {
try {
// Now check the method and field CP entries
for (Iterator iter = pool.iterator(); iter.hasNext(); ) {
Object obj = iter.next();
if (obj instanceof RefCpInfo) {
((CpInfo) obj).markNTRefs(pool);
}
}
} catch (ArrayIndexOutOfBoundsException e) {
throw new IllegalStateException("Inconsistent reference to constant pool.");
}
}
/** Trim attributes from the classfile ('Code', 'Exceptions', 'ConstantValue'
* are preserved, all others except the list in the String[] are killed).
* @param extraAttrs array with additional attributes to keep
*/
public void trimAttrsExcept(String[] extraAttrs) {
// Merge additional attributes with required list
String[] keepAttrs = REQUIRED_ATTRS;
if (extraAttrs != null && extraAttrs.length > 0) {
String[] tmp = new String[keepAttrs.length + extraAttrs.length];
System.arraycopy(keepAttrs, 0, tmp, 0, keepAttrs.length);
System.arraycopy(extraAttrs, 0, tmp, keepAttrs.length, extraAttrs.length);
keepAttrs = tmp;
}
// Traverse all attributes, removing all except those on 'keep' list
for (int i = 0; i < getFieldsCount(); i++) {
getFieldInfo(i).trimAttrsExcept(keepAttrs);
}
for (int i = 0; i < getMethodsCount(); i++) {
getMethodInfo(i).trimAttrsExcept(keepAttrs);
}
for (int i = 0; i < getAttributesCount(); i++) {
if (Tools.isInArray(getAttributeInfo(i).getAttrName(), keepAttrs)) {
getAttributeInfo(i).trimAttrsExcept(keepAttrs);
} else {
setAttributeInfo(i, null);
}
}
// Delete the marked attributes
AttrInfo[] left = new AttrInfo[getAttributesCount()];
int j = 0;
for (int i = 0; i < getAttributesCount(); i++) {
if (null != getAttributeInfo(i)) {
left[j++] = getAttributeInfo(i);
}
}
AttrInfo[] attributes = new AttrInfo[j];
System.arraycopy(left, 0, attributes, 0, j);
setAttributesCount(j);
setAttributeInfo(attributes);
// Update the constant pool reference counts
constantPool.updateRefCount();
}
/** Trim attributes from the classfile ('Code', 'Exceptions', 'ConstantValue'
* are preserved, all others are killed).
*/
public void trimAttrs() {
trimAttrsExcept(null);
}
/** Remap the entities in the current class file.
* @param nm the name mapper for the class file
*/
public void remap(NameMapper nm) {
// Remove unnecessary attributes from the class
String[] attrs = nm.getAttrsToKeep();
if (attrs.length > 0) {
trimAttrsExcept(attrs);
} else {
trimAttrs();
}
// Remap all the package/interface/class/method/field names
//
String thisClassName = ((Utf8CpInfo)getCpEntry(((ClassCpInfo)getCpEntry(getClassIndex())).getClassNameIndex())).getString();
// Remap the 'inner name' reference of the 'InnerClasses' attribute
for (int i = 0; i < getAttributesCount(); i++) {
AttrInfo attrInfo = getAttributeInfo(i);
if (attrInfo instanceof InnerClassesAttrInfo) {
// For each inner class referemce,
InnerClassesInfo[] info = ((InnerClassesAttrInfo) attrInfo).getInnerClasses();
for (int j = 0; j < info.length; j++) {
// Get the 'inner name' (it is a CONSTANT_Utf8)
CpInfo cpInfo = getCpEntry(info[j].getInnerNameIndex());
if (cpInfo instanceof Utf8CpInfo) {
// Get the remapped class name
Utf8CpInfo utf = (Utf8CpInfo) cpInfo;
String origClass = utf.getString();
// Only remap non-anonymous classes (anon are "")
if (!origClass.equals("")) {
// Get the full inner class name
ClassCpInfo innerClassInfo = (ClassCpInfo) getCpEntry(info[j].getInnerClassInfoIndex());
String innerClassName = ((Utf8CpInfo) getCpEntry(innerClassInfo.getClassNameIndex())).getString();
// It is the remapped simple name that must be stored, so truncate it
String remapClass = nm.mapClass(innerClassName);
remapClass = remapClass.substring(remapClass.lastIndexOf('$') + 1);
int remapIndex = constantPool.remapUtf8To(remapClass, info[j].getInnerNameIndex());
info[j].setInnerNameIndex(remapIndex);
}
}
}
}
}
// Remap the 'name' and 'descriptor' references of the 'LocalVariableTable'
// attribute, in the 'Code' attribute of method structures.
for (int i = 0; i < getMethodsCount(); i++) {
for (int j = 0; j < getMethodInfo(i).getAttributesLength(); j++) {
AttrInfo attrInfo = getMethodInfo(i).getAttribute(j);
if (attrInfo instanceof CodeAttrInfo) {
CodeAttrInfo codeAttrInfo = (CodeAttrInfo)attrInfo;
for (int k = 0; k < codeAttrInfo.getAttributesLength(); k++) {
AttrInfo innerAttrInfo = codeAttrInfo.getAttributes()[k];
if (innerAttrInfo instanceof LocalVariableTableAttrInfo) {
LocalVariableTableAttrInfo lvtAttrInfo = (LocalVariableTableAttrInfo)innerAttrInfo;
LocalVariableInfo[] lvts = lvtAttrInfo.getLocalVariableTable();
for (int m = 0; m < lvts.length; m++) {
// Remap name
Utf8CpInfo nameUtf = (Utf8CpInfo)getCpEntry(lvts[m].getNameIndex());
String remapName = nm.mapField(thisClassName, nameUtf.getString());
// if no mapping is found use the original name
if (null == remapName) {
remapName = nameUtf.getString();
}
lvts[m].setNameIndex(constantPool.remapUtf8To(remapName, lvts[m].getNameIndex()));
// Remap descriptor
Utf8CpInfo descUtf = (Utf8CpInfo)getCpEntry(lvts[m].getDescriptorIndex());
String remapDesc = nm.mapDescriptor(descUtf.getString());
lvts[m].setDescriptorIndex(constantPool.remapUtf8To(remapDesc, lvts[m].getDescriptorIndex()));
}
}
}
}
}
}
// Go through all of class's fields and methods mapping 'name' and 'descriptor' references
for (int i = 0; i < getFieldsCount(); i++) {
// Remap field 'name', unless it is 'Synthetic'
FieldInfo field = getFieldInfo(i);
if (!field.isSynthetic()) {
Utf8CpInfo nameUtf = (Utf8CpInfo) getCpEntry(field.getNameIndex());
String remapName = nm.mapField(thisClassName, nameUtf.getString());
// if no mapping is found use the original name
if (null == remapName) {
remapName = nameUtf.getString();
}
field.setNameIndex(constantPool.remapUtf8To(remapName, field.getNameIndex()));
}
// Remap field 'descriptor'
Utf8CpInfo descUtf = (Utf8CpInfo)getCpEntry(field.getDescriptorIndex());
String remapDesc = nm.mapDescriptor(descUtf.getString());
field.setDescriptorIndex(constantPool.remapUtf8To(remapDesc, field.getDescriptorIndex()));
}
for (int i = 0; i < getMethodsCount(); i++) {
// Remap method 'name', unless it is 'Synthetic'
MethodInfo method = getMethodInfo(i);
Utf8CpInfo descUtf = (Utf8CpInfo)getCpEntry(method.getDescriptorIndex());
if (!method.isSynthetic()) {
Utf8CpInfo nameUtf = (Utf8CpInfo)getCpEntry(method.getNameIndex());
String remapName = nm.mapMethod(thisClassName, nameUtf.getString(), descUtf.getString());
// if no mapping is found use the original name
if (null == remapName) {
remapName = nameUtf.getString();
}
method.setNameIndex(constantPool.remapUtf8To(remapName, method.getNameIndex()));
}
// Remap method 'descriptor'
String remapDesc = nm.mapDescriptor(descUtf.getString());
method.setDescriptorIndex(constantPool.remapUtf8To(remapDesc, method.getDescriptorIndex()));
}
// Remap all field/method names and descriptors in the constant pool (depends on class names)
int currentCpLength = constantPool.length(); // constant pool can be extended (never contracted) during loop
for (int i = 0; i < currentCpLength; i++) {
CpInfo cpInfo = getCpEntry(i);
if (null != cpInfo) {
// If this is a CONSTANT_Fieldref, CONSTANT_Methodref or CONSTANT_InterfaceMethodref
// get the CONSTANT_NameAndType and remap the name and the components of the
// descriptor string.
if (cpInfo instanceof RefCpInfo) {
// Get the unmodified class name
ClassCpInfo classInfo = (ClassCpInfo)getCpEntry(((RefCpInfo)cpInfo).getClassIndex());
Utf8CpInfo classUtf = (Utf8CpInfo)getCpEntry(classInfo.getClassNameIndex());
String className = classUtf.getString();
// Get the current N&T reference and its 'name' and 'descriptor' utf's
int ntIndex = ((RefCpInfo)cpInfo).getNameAndTypeIndex();
NameAndTypeCpInfo nameTypeInfo = (NameAndTypeCpInfo)getCpEntry(ntIndex);
Utf8CpInfo refUtf = (Utf8CpInfo)getCpEntry(nameTypeInfo.getNameIndex());
Utf8CpInfo descUtf = (Utf8CpInfo)getCpEntry(nameTypeInfo.getDescriptorIndex());
// Get the remapped versions of 'name' and 'descriptor'
String remapRef;
if (cpInfo instanceof FieldrefCpInfo) {
remapRef = nm.mapField(className, refUtf.getString());
// if no mapping is found use the original name
if (null == remapRef) {
remapRef = refUtf.getString();
}
// cloder - check if this is a compiler generated field
// supporting the JDK1.2-or-later '.class' construct
if (refUtf.getString().startsWith("class$")) {
String realClassName = refUtf.getString().substring(6).replace('$', '.');
// check whether the hardcoded class name is not a class from the
// Java SDK, i.e. doesn't start with "java." or "javax."
if (!realClassName.startsWith("java.") && !realClassName.startsWith("javax.")
&& !realClassName.startsWith("sun.") && !realClassName.startsWith("com.sun.")) {
// check whether the hardcoded class name exists in the main tree
// whether it is be obfuscated
Cl cl = getClassTree().findClass(realClassName);
if (null != cl) {
if (cl.isBeingObfuscated()) {
FileLogger.getInstance().addWarning("# WARNING: " + realClassName +
" shouldn't be obfuscated: it is referenced as "
+ realClassName + ".class from " + thisClassName);
}
}
}
}
// end cloder
} else {
// instance of method mapping
remapRef = nm.mapMethod(className, refUtf.getString(), descUtf.getString());
// if no mapping is found use the original name
if (null == remapRef) {
remapRef = refUtf.getString();
}
}
String remapDesc = nm.mapDescriptor(descUtf.getString());
// If a remap is required, make a new N&T (increment ref count on 'name' and
// 'descriptor', decrement original N&T's ref count, set new N&T ref count to 1),
// remap new N&T's utf's
if (!remapRef.equals(refUtf.getString()) ||
!remapDesc.equals(descUtf.getString())) {
// Get the new N&T guy
NameAndTypeCpInfo newNameTypeInfo;
if (nameTypeInfo.getRefCount() == 1) {
newNameTypeInfo = nameTypeInfo;
}
else {
// Create the new N&T info
newNameTypeInfo = (NameAndTypeCpInfo)nameTypeInfo.clone();
// Adjust its reference counts of its utf's
((CpInfo)getCpEntry(newNameTypeInfo.getNameIndex())).incRefCount();
((CpInfo)getCpEntry(newNameTypeInfo.getDescriptorIndex())).incRefCount();
// Append it to the Constant Pool, and
// point the RefCpInfo entry to the new N&T data
((RefCpInfo)cpInfo).setNameAndTypeIndex(
constantPool.addEntry(newNameTypeInfo));
// Adjust reference counts from RefCpInfo
newNameTypeInfo.incRefCount();
nameTypeInfo.decRefCount();
}
// Remap the 'name' and 'descriptor' utf's in N&T
newNameTypeInfo.setNameIndex(constantPool.remapUtf8To(remapRef, newNameTypeInfo.getNameIndex()));
newNameTypeInfo.setDescriptorIndex(constantPool.remapUtf8To(remapDesc, newNameTypeInfo.getDescriptorIndex()));
}
}
}
}
// Finally, remap all class references to Utf
for (int i = 0; i < constantPool.length(); i++) {
CpInfo cpInfo = getCpEntry(i);
if (cpInfo != null) {
// If this is CONSTANT_Class, remap the class-name Utf8 entry
if (cpInfo instanceof ClassCpInfo) {
ClassCpInfo classInfo = (ClassCpInfo)cpInfo;
Utf8CpInfo utf = (Utf8CpInfo)getCpEntry(classInfo.getClassNameIndex());
String remapClass = nm.mapClass(utf.getString());
int remapIndex = constantPool.remapUtf8To(remapClass, classInfo.getClassNameIndex());
classInfo.setClassNameIndex(remapIndex);
}
}
}
}
/** Export the representation of the class file to a DataOutput stream.
* @param dout the output stream
* @throws IOException if an I/O error occurs
*/
public void write(DataOutput dout)
throws IOException {
if (dout == null) throw new IOException("No output stream was provided.");
dout.writeInt(getMagic());
dout.writeShort(getMinorVersion());
dout.writeShort(getMajorVersion());
dout.writeShort(constantPool.length());
for (Iterator iter = constantPool.iterator(); iter.hasNext(); ) {
CpInfo cpInfo = (CpInfo) iter.next();
if (cpInfo != null) {
cpInfo.write(dout);
}
}
dout.writeShort(getAccessFlags());
dout.writeShort(getClassIndex());
dout.writeShort(getSuperClassIndex());
dout.writeShort(getInterfacesCount());
for (int i = 0; i < getInterfacesCount(); i++) {
dout.writeShort(getInterface(i));
}
dout.writeShort(getFieldsCount());
for (int i = 0; i < getFieldsCount(); i++) {
getFieldInfo(i).write(dout);
}
dout.writeShort(getMethodsCount());
for (int i = 0; i < getMethodsCount(); i++) {
getMethodInfo(i).write(dout);
}
dout.writeShort(getAttributesCount());
for (int i = 0; i < getAttributesCount(); i++) {
getAttributeInfo(i).write(dout);
}
}
/** Dump the content of the class file to the specified file (used for
* debugging).
* @param pw the print writer
*/
public void dump(PrintWriter pw) {
pw.println("_____________________________________________________________________");
pw.println("CLASS: " + getName());
pw.println("Magic: " + Integer.toHexString(getMagic()) + " (" + getMagic() + ")");
pw.println("Minor version: " + Integer.toHexString(getMinorVersion()) + " (" + getMinorVersion() + ")");
pw.println("Major version: " + Integer.toHexString(getMajorVersion()) + " (" + getMajorVersion() + ")");
pw.println();
pw.println("Access: " + getAccessFlags());
pw.println("This class: " + getClassIndex() + " / " + getName());
pw.println("Superclass: " + getSuperClassIndex() + " / " + getSuper());
pw.println();
pw.println("CP length: " + constantPool.length());
for (int i = 0; i < constantPool.length(); i++) {
CpInfo cpInfo = (CpInfo) constantPool.getCpEntry(i);
if (cpInfo != null) {
cpInfo.dump(pw, this, i);
}
}
pw.println("Attributes: " + getAttributesCount());
for (int i = 0; i < getAttributesCount(); i++) {
pw.println();
pw.print("attribute[" + i + "]: ");
getAttributeInfo(i).dump(pw, this);
}
pw.println();
pw.println("Direct implemented interfaces: " + getInterfacesCount());
for (int i = 0; i < getInterfacesCount(); i++) {
CpInfo info = getCpEntry(getInterface(i));
pw.print(" interface[" + i + "]: ");
if (null == info) {
pw.println("(null)");
} else {
pw.println( ((Utf8CpInfo) getCpEntry( ((ClassCpInfo) info).getClassNameIndex())).getString() );
}
}
pw.println();
pw.println("Declared fields: " + getFieldsCount());
for (int i = 0; i < getFieldsCount(); i++) {
ClassItemInfo info = getFieldInfo(i);
pw.print(" field[" + i + "]: ");
if (null == info) {
pw.println("(null)");
} else {
pw.println(" name: " + ((Utf8CpInfo) getCpEntry(info.getNameIndex())).getString());
pw.println(" descriptor: " + ((Utf8CpInfo) getCpEntry(info.getDescriptorIndex())).getString());
}
pw.println(" attributes: " + info.getAttributesLength());
for (int j = 0; j < info.getAttributesLength(); j++) {
pw.print(" attribute[" + j + "]: ");
info.getAttribute(j).dump(pw, this);
pw.println();
}
}
pw.println();
pw.println("Declared methods: " + getMethodsCount());
for (int i = 0; i < getMethodsCount(); i++) {
ClassItemInfo info = getMethodInfo(i);
pw.print(" method[" + i + "]: ");
if (info == null) {
pw.println("(null)");
} else {
pw.println();
pw.println(" name: " + ((Utf8CpInfo) getCpEntry(info.getNameIndex())).getString());
pw.println(" descriptor: " + ((Utf8CpInfo) getCpEntry(info.getDescriptorIndex())).getString());
pw.println(" access flags: " + info.getAccessFlags());
}
pw.println(" attributes: " + info.getAttributesLength());
for (int j = 0; j < info.getAttributesLength(); j++) {
pw.print(" attribute[" + j + "]: ");
info.getAttribute(j).dump(pw, this);
pw.println();
}
}
}
/** Retrieve a list of classes that are used in hardcoded references.
* @return a vector containing strings with class names
* @see #remap
*/
public Set getHardcodedClassNames() {
HashSet result = new HashSet();
int currentCpLength = constantPool.length();
for (int i = 0; i < currentCpLength; i++) {
CpInfo cpInfo = getCpEntry(i);
if (cpInfo != null && cpInfo instanceof RefCpInfo) {
// Get the current name and type reference and its 'name' utf's
int ntIndex = ((RefCpInfo) cpInfo).getNameAndTypeIndex();
NameAndTypeCpInfo nameTypeInfo = (NameAndTypeCpInfo) getCpEntry(ntIndex);
Utf8CpInfo refUtf = (Utf8CpInfo) getCpEntry(nameTypeInfo.getNameIndex());
if (cpInfo instanceof FieldrefCpInfo) {
// cloder - check if this is a compiler generated field
// supporting the JDK1.2-or-later '.class' construct
if (refUtf.getString().startsWith("class$")) {
String realClassName = refUtf.getString().substring(6).replace('$', '.');
if (!realClassName.startsWith("java.") && !realClassName.startsWith("javax.")
&& !realClassName.startsWith("sun.") && !realClassName.startsWith("com.sun.")) {
result.add(realClassName);
}
}
// end cloder
}
}
}
return result;
}
}
|