Use BytecodeInfo, much much much simpler now :-)

Do some code transformations


git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@469 379699f6-c40d-0410-875b-85095c16579e
stable
jochen 26 years ago
parent 9267e857f7
commit 5cb3c63388
  1. 558
      jode/jode/obfuscator/MethodIdentifier.java

@ -28,12 +28,16 @@ import java.util.Hashtable;
public class MethodIdentifier extends Identifier implements Opcodes { 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 * The exceptions that can be thrown by this method
*/ */
String[] exceptions; String[] exceptions;
/**
* The byte code of this method, or null if there isn't any.
*/
BytecodeInfo bytecode;
public MethodIdentifier(ClassIdentifier clazz, MethodInfo info) { public MethodIdentifier(ClassIdentifier clazz, MethodInfo info) {
super(info.getName()); super(info.getName());
this.clazz = clazz; this.clazz = clazz;
@ -46,8 +50,8 @@ public class MethodIdentifier extends Identifier implements Opcodes {
if (codeattr != null) { if (codeattr != null) {
DataInputStream stream = new DataInputStream DataInputStream stream = new DataInputStream
(new ByteArrayInputStream(codeattr.getContents())); (new ByteArrayInputStream(codeattr.getContents()));
codeinfo = new CodeInfo(); bytecode = new BytecodeInfo();
codeinfo.read(clazz.info.getConstantPool(), stream); bytecode.read(clazz.info.getConstantPool(), stream);
} }
if (exceptionsattr != null) if (exceptionsattr != null)
readExceptions(exceptionsattr); readExceptions(exceptionsattr);
@ -63,110 +67,21 @@ public class MethodIdentifier extends Identifier implements Opcodes {
} }
} }
/**
* Skips the specified number of bytes in the input stream. This calls
* skip as long until the bytes are all skipped.
* @param is the inputstream to skip.
* @param count the number of bytes to skip.
*/
private final static void skip(InputStream is, long count)
throws IOException {
while (count > 0) {
long skipped = is.skip(count);
if (skipped == 0)
throw new EOFException("Can't skip.");
count -= skipped;
}
}
/** /**
* Reads the opcodes out of the code info and determine its * Reads the opcodes out of the code info and determine its
* references * references
* @return an enumeration of the references. * @return an enumeration of the references.
*/ */
public void analyzeCode() throws IOException{ public void analyzeCode() throws IOException {
ConstantPool cp = clazz.info.getConstantPool(); for (Instruction instr = bytecode.getFirstInstr();
byte[] code = codeinfo.getCode(); instr != null; instr = instr.nextByAddr) {
DataInputStream stream = switch (instr.opcode) {
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:
skip(stream, 2);
addr+=4;
break;
case opc_iinc:
skip(stream, 4);
addr+=6;
break;
default:
throw new ClassFormatError("Invalid wide opcode "+opcode);
}
break;
}
case opc_ret:
skip(stream, 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:
skip(stream, 2);
addr+=3;
break;
case opc_jsr_w:
case opc_goto_w:
skip(stream, 4);
addr+=5;
break;
case opc_tableswitch: {
int length = 7-(addr % 4);
skip(stream, length);
int low = stream.readInt();
int high = stream.readInt();
skip(stream, 4*(high-low+1));
addr += 9 + length + 4*(high-low+1);
break;
}
case opc_lookupswitch: {
int length = 7-(addr % 4);
skip(stream, length);
int npairs = stream.readInt();
skip(stream, 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_new:
case opc_anewarray: case opc_anewarray:
case opc_checkcast: case opc_checkcast:
case opc_instanceof: case opc_instanceof:
case opc_multianewarray: { case opc_multianewarray: {
int ref = stream.readUnsignedShort(); String clName = (String) instr.objData;
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++)
@ -180,46 +95,21 @@ public class MethodIdentifier extends Identifier implements Opcodes {
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) {
skip(stream, 1);
addr ++;
}
break; break;
} }
case opc_getstatic:
case opc_getfield:
case opc_invokespecial: case opc_invokespecial:
case opc_invokestatic: case opc_invokestatic:
case opc_invokeinterface: case opc_invokeinterface:
case opc_invokevirtual: { case opc_invokevirtual: {
int ref = stream.readUnsignedShort(); String[] names = (String[]) instr.objData;
String[] names = cp.getRef(ref);
clazz.bundle.reachableIdentifier clazz.bundle.reachableIdentifier
(names[0].replace('/','.')+"."+names[1]+"."+names[2], (names[0].replace('/','.')+"."+names[1]+"."+names[2],
opcode == opc_invokevirtual instr.opcode == opc_invokevirtual
|| opcode == opc_invokeinterface); || instr.opcode == opc_invokeinterface);
addr += 3;
if (opcode == opc_invokeinterface) {
skip(stream, 2);
addr += 2;
}
break; break;
} }
default:
if (opcode == opc_newarray
|| (opcode >= opc_bipush && opcode <= opc_aload)
|| (opcode >= opc_istore && opcode <= opc_astore)) {
skip(stream, 1);
addr += 2;
} else if (opcode >= opc_ifeq && opcode <= opc_jsr) {
skip(stream, 2);
addr += 3;
} else if (opcode == opc_xxxunusedxxx
|| opcode >= opc_breakpoint)
throw new ClassFormatError("Invalid opcode "+opcode);
else
addr++;
} }
} }
} }
@ -254,7 +144,7 @@ public class MethodIdentifier extends Identifier implements Opcodes {
index = type.indexOf('L', end); index = type.indexOf('L', end);
} }
if (codeinfo != null) { if (bytecode != null) {
try { try {
analyzeCode(); analyzeCode();
} catch (IOException ex) { } catch (IOException ex) {
@ -334,152 +224,95 @@ public class MethodIdentifier extends Identifier implements Opcodes {
out.write(buff, 0, length); out.write(buff, 0, length);
} }
public void transformCode(GrowableConstantPool gcp) { /**
ConstantPool cp = clazz.info.getConstantPool(); * This method does the code transformation. This include
byte[] origcode = codeinfo.getCode(); * <ul><li>new slot distribution for locals</li>
ByteArrayOutputStream baos = new ByteArrayOutputStream(); * <li>obfuscating transformation of flow</li>
DataOutputStream output = new DataOutputStream(baos); * <li>renaming field, method and class references</li>
DataInputStream input = new DataInputStream * </ul>
(new ByteArrayInputStream(origcode)); */
try { public void doCodeTransformations(GrowableConstantPool gcp) {
output.writeShort(codeinfo.getMaxStack()); if (bytecode != null) {
output.writeShort(codeinfo.getMaxLocals()); /* XXX This should be in a if (Obfuscator.distributeLocals) */
output.writeInt(origcode.length); LocalOptimizer localOpt = new LocalOptimizer(bytecode);
int addr = 0; localOpt.calcLocalInfo();
while (input.available() > 0) { localOpt.distributeLocals();
int opcode = input.readUnsignedByte(); if (Obfuscator.isDebugging)
switch (opcode) { localOpt.dumpLocals();
case opc_wide: {
output.writeByte(opcode); for (Instruction instr = bytecode.getFirstInstr();
int wideopcode = input.readUnsignedByte(); instr != null; instr = instr.nextByAddr) {
switch (wideopcode) { switch (instr.opcode) {
case opc_iload: case opc_lload: case opc_ldc:
case opc_fload: case opc_dload: case opc_aload: gcp.reserveConstant(instr.objData);
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; break;
case opc_ldc: { case opc_invokespecial:
int index = input.readUnsignedByte(); case opc_invokestatic:
int newIndex = gcp.copyConstant(cp, index); case opc_invokeinterface:
output.writeByte(opcode); case opc_invokevirtual: {
output.writeByte(newIndex); String[] names = (String[]) instr.objData;
addr+=2; ClassIdentifier ci = (ClassIdentifier)
break; clazz.bundle.getIdentifier(names[0].replace('/','.'));
}
case opc_ldc_w: if (ci != null) {
case opc_ldc2_w: { names[0] = ci.getFullAlias();
int index = input.readUnsignedShort(); names[1] =
int newIndex = gcp.copyConstant(cp, index); ((MethodIdentifier)
output.writeByte(opcode); ci.getIdentifier(names[1], names[2])).getAlias();
output.writeShort(newIndex); }
addr += 3; names[2] = clazz.bundle.getTypeAlias(names[2]);
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; break;
} }
case opc_lookupswitch: { case opc_getstatic:
output.writeByte(opcode); case opc_getfield: {
int length = 7-(addr % 4); String[] names = (String[]) instr.objData;
copy(input, output, length); ClassIdentifier ci = (ClassIdentifier)
int npairs = input.readInt(); clazz.bundle.getIdentifier(names[0].replace('/','.'));
output.writeInt(npairs); if (ci != null) {
copy(input, output, 8*npairs); FieldIdentifier fi = (FieldIdentifier)
addr += 5 + length + 8*npairs; ci.getIdentifier(names[1], names[2]);
names[0] = ci.getFullAlias();
names[1] = fi.getAlias();
}
names[2] = clazz.bundle.getTypeAlias(names[2]);
break; break;
} }
case opc_getstatic:
case opc_getfield:
case opc_putstatic: case opc_putstatic:
case opc_putfield: { case opc_putfield: {
int ref = input.readUnsignedShort(); String[] names = (String[]) instr.objData;
String[] names = cp.getRef(ref);
ClassIdentifier ci = (ClassIdentifier) ClassIdentifier ci = (ClassIdentifier)
clazz.bundle.getIdentifier(names[0].replace('/','.')); clazz.bundle.getIdentifier(names[0].replace('/','.'));
if (ci != null) { if (ci != null) {
names[0] = ci.getFullAlias(); FieldIdentifier fi = (FieldIdentifier)
Identifier fi = ci.getIdentifier(names[1], names[2]); ci.getIdentifier(names[1], names[2]);
if (fi instanceof FieldIdentifier) { if (Obfuscator.shouldStrip && !fi.isReachable()) {
if (Obfuscator.shouldStrip /* Replace instruction with pop opcodes. */
&& !((FieldIdentifier)fi).isReachable()) { int stacksize =
if (opcode != opc_putfield (instr.opcode
&& opcode != opc_putstatic) == Instruction.opc_putstatic) ? 0 : 1;
throw new jode.AssertError stacksize += jode.Type.tType(names[2]).stackSize();
("reading not reachable field"); if (stacksize == 3) {
int stacksize = /* Add a pop instruction after this opcode. */
(opcode == opc_putstatic) ? 0 : 1; Instruction second = new Instruction();
stacksize += jode.Type.tType second.length = 1;
(names[2]).stackSize(); second.opcode = Instruction.opc_pop;
if (stacksize == 3) { second.nextByAddr = instr.nextByAddr;
output.writeByte(opc_pop2); instr.nextByAddr = second;
output.writeByte(opc_pop); second.nextByAddr.preds.removeElement(instr);
output.writeByte(opc_nop); second.nextByAddr.preds.addElement(second);
} else if (stacksize == 2) { stacksize--;
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(); instr.objData = null;
instr.intData = 0;
instr.opcode = Instruction.opc_pop - 1 + stacksize;
instr.length = 1;
} else {
names[0] = ci.getFullAlias();
names[1] = fi.getAlias();
} }
} }
names[2] = clazz.bundle.getTypeAlias(names[2]); names[2] = clazz.bundle.getTypeAlias(names[2]);
output.writeByte(opcode);
output.writeShort(gcp.putRef(cp.getTag(ref), names));
addr += 3;
break; break;
} }
case opc_new: case opc_new:
@ -487,200 +320,29 @@ public class MethodIdentifier extends Identifier implements Opcodes {
case opc_checkcast: case opc_checkcast:
case opc_instanceof: case opc_instanceof:
case opc_multianewarray: { case opc_multianewarray: {
int ref = input.readUnsignedShort(); String clName = (String) instr.objData;
String clName = cp.getClassName(ref).replace('/','.');
if (clName.charAt(0) == '[') { if (clName.charAt(0) == '[') {
clName = clazz.bundle.getTypeAlias(clName); clName = clazz.bundle.getTypeAlias(clName);
} else { } else {
ClassIdentifier ci = (ClassIdentifier) ClassIdentifier ci = (ClassIdentifier)
clazz.bundle.getIdentifier(clName); clazz.bundle.getIdentifier(clName);
if (ci != null) if (ci != null)
clName = ci.getFullAlias(); clName = ci.getFullAlias();
} }
int newRef = gcp.putClassRef(clName); instr.objData = 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; 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(); Handler[] handlers = bytecode.getExceptionHandlers();
output.writeShort(handlers.length / 4); for (int i=0; i< handlers.length; i++) {
for (int i=0; i< handlers.length; i += 4) { if (handlers[i].type != null) {
output.writeShort(handlers[i]); ClassIdentifier ci = (ClassIdentifier)
output.writeShort(handlers[i+1]); clazz.bundle.getIdentifier(handlers[i].type);
output.writeShort(handlers[i+2]);
if (handlers[i+3] == 0)
output.writeShort(0);
else {
String clName
= cp.getClassName(handlers[i+3]).replace('/','.');
ClassIdentifier ci = (ClassIdentifier)
clazz.bundle.getIdentifier(clName);
if (ci != null) if (ci != null)
clName = ci.getFullAlias(); handlers[i].type = ci.getFullAlias();
output.writeShort(gcp.putClassRef(clName));
}
}
output.writeShort(0); // No Attributes;
output.close();
} catch (IOException ex) {
ex.printStackTrace(Obfuscator.err);
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);
addr += 2;
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:
skip(stream, 2);
addr+=4;
break;
case opc_iinc:
skip(stream, 4);
addr+=6;
break;
default:
throw new ClassFormatError("Invalid wide opcode "+opcode);
}
break;
}
case opc_tableswitch: {
int length = 7-(addr % 4);
skip(stream, length);
int low = stream.readInt();
int high = stream.readInt();
skip(stream, 4*(high-low+1));
addr += 9 + length + 4*(high-low+1);
break;
}
case opc_lookupswitch: {
int length = 7-(addr % 4);
skip(stream, length);
int npairs = stream.readInt();
skip(stream, 8*npairs);
addr += 5 + length + 8*npairs;
break;
}
case opc_ret:
skip(stream, 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_new:
case opc_anewarray:
case opc_checkcast:
case opc_instanceof:
skip(stream, 2);
addr+=3;
break;
case opc_multianewarray:
skip(stream, 3);
addr += 4;
break;
case opc_jsr_w:
case opc_goto_w:
case opc_invokeinterface:
skip(stream, 4);
addr+=5;
break;
default:
if (opcode == opc_newarray
|| (opcode >= opc_bipush && opcode <= opc_aload)
|| (opcode >= opc_istore && opcode <= opc_astore)) {
skip(stream, 1);
addr += 2;
} else if (opcode >= opc_ifeq && opcode <= opc_jsr
|| opcode >= opc_getstatic && opcode <= opc_invokestatic) {
skip(stream, 2);
addr += 3;
} else if (opcode == opc_xxxunusedxxx
|| opcode >= opc_breakpoint)
throw new ClassFormatError("Invalid opcode "+opcode);
else
addr++;
}
} }
} catch (IOException ex) {
ex.printStackTrace(Obfuscator.err);
} }
} }
} }
@ -690,10 +352,20 @@ public class MethodIdentifier extends Identifier implements Opcodes {
descriptorIndex = gcp.putUTF(clazz.bundle.getTypeAlias(getType())); descriptorIndex = gcp.putUTF(clazz.bundle.getTypeAlias(getType()));
codeIndex = 0; codeIndex = 0;
if (codeinfo != null) { if (bytecode != null) {
transformCode(gcp);
if (code != null)
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream output = new DataOutputStream(baos);
try {
bytecode.writeCode(gcp, clazz.bundle, output);
output.close();
code = baos.toByteArray();
codeIndex = gcp.putUTF("Code"); codeIndex = gcp.putUTF("Code");
} catch (IOException ex) {
code = null;
}
} }
if (exceptions != null) { if (exceptions != null) {
exceptionsIndex = gcp.putUTF("Exceptions"); exceptionsIndex = gcp.putUTF("Exceptions");

Loading…
Cancel
Save