Package com.lambda.Debugger

Source Code of com.lambda.Debugger.InstrumentorForCL

package com.lambda.Debugger;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.jar.Attributes;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.zip.CRC32;
import java.util.zip.ZipEntry;

import org.apache.bcel.Constants;
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.ArrayType;
import org.apache.bcel.generic.ClassGen;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.INVOKESPECIAL;
import org.apache.bcel.generic.INVOKESTATIC;
import org.apache.bcel.generic.INVOKEVIRTUAL;
import org.apache.bcel.generic.Instruction;
import org.apache.bcel.generic.InstructionConstants;
import org.apache.bcel.generic.InstructionFactory;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.InstructionList;
import org.apache.bcel.generic.InvokeInstruction;
import org.apache.bcel.generic.MethodGen;
import org.apache.bcel.generic.PUSH;
import org.apache.bcel.generic.Type;

/**
*/

public class InstrumentorForCL {
    private static Class debugClassLoader;
    private static java.lang.reflect.Method method;


    // Instrument ClassLoader.class to call InstrumentorForCL.debugify() before defineClass0(). Then write
    // both files out to ClassLoader.jar.
    public void instrument(String destJar) throws Exception {
        File dest = new File(destJar);
        if (dest.exists() && !dest.canWrite()) {
            throw new Exception(destJar + " exists and is not writable");
        }

        // patch the java.lang.ClassLoader
        InputStream is = ClassLoader.getSystemClassLoader().getParent().getResourceAsStream("java/lang/ClassLoader.class");
        byte[] bytes = inputStreamToByteArray(is);
        is.close();
        byte[] patched = patchCL(bytes);


  // Include this file in the ajr
        is = ClassLoader.getSystemClassLoader().getResourceAsStream("com/lambda/Debugger/InstrumentorForCL.class");
        bytes = inputStreamToByteArray(is);
        is.close();

        // pack the jar file
        Manifest mf = new Manifest();
        Attributes at = mf.getMainAttributes();
        at.putValue(Attributes.Name.MANIFEST_VERSION.toString(), "1.0");
        at.putValue("Created-By", "ODB [java " + System.getProperty("java.version") + "]");
        CRC32 crc = new CRC32();
        JarOutputStream jar = new JarOutputStream(new FileOutputStream(dest), mf);

  {
        ZipEntry entry = new ZipEntry("java/lang/ClassLoader.class");
        entry.setSize(patched.length);
        entry.setCrc(crc.getValue());
        crc.update(patched);
        jar.putNextEntry(entry);
        jar.write(patched);
        jar.closeEntry();
  }
  {
        ZipEntry entry = new ZipEntry("com/lambda/Debugger/InstrumentorForCL.class");
        entry.setSize(bytes.length);
        entry.setCrc(crc.getValue());
        crc.update(bytes);
        jar.putNextEntry(entry);
        jar.write(bytes);
        jar.closeEntry();
  }

        jar.close();
    }

    public static byte[] inputStreamToByteArray(InputStream is) throws IOException {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        for (int b = is.read(); b != -1; b = is.read()) {
            os.write(b);
        }
        return os.toByteArray();
    }


    public byte[] patchCL(byte[] b) {
        try {

            final ClassGen cg = new ClassGen(new ClassParser(new ByteArrayInputStream(b), "<generated>").parse());
            final String className = cg.getClassName();
            final Method[] methods = cg.getMethods();
            final ConstantPoolGen cpg = cg.getConstantPool();
            final InstructionFactory factory = new InstructionFactory(cg);

            // for all methods, look for caller side "this.define0" calls
            for (int i = 0; i < methods.length; i++) {
                final MethodGen mg = new MethodGen(methods[i], className, cpg);
                final InstructionList il = mg.getInstructionList();
                if (il == null) continue;
                InstructionHandle ih = il.getStart();
    String methodName = methods[i].getName();

                while (ih != null) {
                    final Instruction ins = ih.getInstruction();
                    if (ins instanceof INVOKESPECIAL
                            || ins instanceof INVOKESTATIC
                            || ins instanceof INVOKEVIRTUAL) {
                        final InvokeInstruction invokeInst = (InvokeInstruction)ins;
                        final String callerSideMethodClassName = invokeInst.getClassName(cpg);
                        final String callerSideMethodName = invokeInst.getMethodName(cpg);

                        if ("java.lang.ClassLoader".equals(callerSideMethodClassName)
                                && "defineClass0".equals(callerSideMethodName)) {

                            //assert compliant JRE
                            Type args[] = invokeInst.getArgumentTypes(cpg);
                            assertSupported(args);

                            // store former method args in local vars
                            InstructionHandle ihc = null;
                            if (args.length > 5) {
                                // IBM like JRE with extra args
                                ihc = il.append(ih.getPrev(), factory.createStore(args[args.length - 1], 2100 + args.length - 1));
                                for (int index = args.length - 2; index >= 5; index--) {
                                    ihc = il.append(ihc, InstructionFactory.createStore(args[index], 2100 + index));
                                }
                                ihc = il.append(ihc, factory.createStore(Type.OBJECT, 2016));//protection domain
                            }
                            else {
                                // SUN regular JRE
                                ihc = il.append(ih.getPrev(), factory.createStore(Type.OBJECT, 2016));//protection domain
                            }

                            ihc = il.append(ihc, factory.createStore(Type.INT, 2015));//length
                            ihc = il.append(ihc, factory.createStore(Type.INT, 2014));//index
                            ihc = il.append(ihc, factory.createStore(Type.OBJECT, 2013));//bytes
                            ihc = il.append(ihc, factory.createStore(Type.OBJECT, 2012));//name

                            // prepare method call stack
                            ihc = il.append(ihc, factory.createLoad(Type.OBJECT, 2012));
                            ihc = il.append(ihc, factory.createLoad(Type.OBJECT, 2013));
                            ihc = il.append(ihc, factory.createInvoke(
                                    "com.lambda.Debugger.InstrumentorForCL",
                                    "debugify",
                                    new ArrayType(Type.BYTE, 1),
                                    new Type[]{
                                        Type.STRING,
                                        new ArrayType(Type.BYTE, 1),
            },
                                    Constants.INVOKESTATIC));
                            ihc = il.append(ihc, factory.createStore(Type.OBJECT, 3018));//result bytes

                            // rebuild former method call stack
                            ihc = il.append(ihc, factory.createLoad(Type.OBJECT, 2012));//name
                            ihc = il.append(ihc, factory.createLoad(Type.OBJECT, 3018));//bytes
                            ihc = il.append(ihc, new PUSH(cpg, 0));
                            ihc = il.append(ihc, factory.createLoad(Type.OBJECT, 3018));//bytes
                            ihc = il.append(ihc, InstructionConstants.ARRAYLENGTH);//.length
                            ihc = il.append(ihc, factory.createLoad(Type.OBJECT, 2016));//protection domain

                            // extra args for IBM like JRE
                            if (args.length > 5) {
                                for (int index = 5; index < args.length; index++) {
                                    ihc = il.append(ihc, factory.createLoad(args[index], 2100 + index));
                                }
                            }
                        }
                    }
                    ih = ih.getNext();
                }
                mg.setInstructionList(il);
                mg.setMaxLocals();
                mg.setMaxStack();
                methods[i] = mg.getMethod();
            }
            cg.setMethods(methods);
            return cg.getJavaClass().getBytes();
        }
        catch (Exception e) {
            System.err.println("failed to patch ClassLoader:");
            e.printStackTrace();
            return b;
        }
    }



    /**
     * Check the signature of defineClass0
     * @param args
     */
    private static void assertSupported(Type[] args) {
        if (args.length >= 5 &&
                (
                args[0].getSignature().equals("Ljava/lang/String;")
                && args[1].getSignature().equals("[B")
                && args[2].getSignature().equals("I")
                && args[3].getSignature().equals("I")
                && args[4].getSignature().equals("Ljava/security/ProtectionDomain;")
                ))
            ;
        else {
            StringBuffer sign = new StringBuffer("(");
            for (int i = 0; i < args.length; i++) {
                sign.append(args[i].toString());
                if (i < args.length - 1)
                    sign.append(", ");
            }
            sign.append(")");
            throw new Error("non standard JDK, native call not supported " + sign.toString());
        }
    }



    // Use reflection to avoid putting the Debugger on bootclasspath. NO! ON PATH!
    public static synchronized byte[] debugify(String name, byte[] b) {
  if (name.startsWith("java")) return b;
  if (name.startsWith("JAVAX")) return b;
  if (name.startsWith("edu.insa.LSD")) return b;
  if (name.startsWith("lambda.Debugger")) return b;
  if (name.startsWith("com.lambda.Debugger")) return b;
  if (name.startsWith("org.apache.bcel")) return b;
  //  System.out.println("Defining: " + name);
  try {
      if (debugClassLoader == null) {  // NB: Not using this as a classloader, just calling debugify().
    debugClassLoader = ClassLoader.getSystemClassLoader().loadClass("com.lambda.Debugger.DebugifyingClassLoader");
    method = debugClassLoader.getDeclaredMethod("debugify", new Class[] { String.class, byte[].class });
      }

      Object[] argList = new Object[] {name, b};
      byte[] patched = (byte[])method.invoke(null, argList)// DebugifyingClassLoader.debugify("com.l.MyClass", byte[..])
      return patched;
 
  catch (Exception e) {
      System.out.println("ODB: IMPOSSIBLE. Missing debugify");
      e.printStackTrace();
      System.exit(1);
  }
  return b;
    }



    public static void main(String args[]) throws Exception {
  String destination = "/Users/bil/Debugger/com";
  if (args.length > 0) destination = args[0];
  destination += "ClassLoader.jar";
  InstrumentorForCL ins = new InstrumentorForCL();
  ins.instrument(destination);
  System.out.println("Patched ClassLoader written to: " + destination);
    }


}
TOP

Related Classes of com.lambda.Debugger.InstrumentorForCL

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.