如何使用ASM来欺骗HelloWorld.scala?加载到JVM但未找到?

时间:2012-12-21 13:16:32

标签: java scala classloader java-bytecode-asm

我使用自定义类加载器将ASM与java一起使用,但我在scala中遇到同样的问题。什么是首选方法?

HelloScala.scala编译为两个类(HelloScala.class HelloScala$.class)。我是否需要欺骗两者的字节码?

我的代码似乎只填充了一个HelloScala$.class,但没有公共构造函数或方法。我可以使用Reflection API并使用Constructor来获取访问权限,但有两个问题:

  1. 忽略HelloScala.class,我错过了什么有价值的东西吗?
  2. 这有危险还是有臭味?
  3. “正确”的方式可能是调用main中的公开HelloScala,但我收到此错误:

    [Loaded HelloScala from __JVM_DefineClass__]
    [Loaded scala.ScalaObject from file:/home/julianpeeters/asm-scala-example/lib/scala-library-2.9.1.jar]
    [Loaded HelloScala$ from __JVM_DefineClass__]
    [Loaded sun.reflect.NativeMethodAccessorImpl from /usr/lib/jvm/java-6-openjdk/jre/lib/rt.jar]
    [Loaded sun.reflect.DelegatingMethodAccessorImpl from /usr/lib/jvm/java-6-openjdk/jre/lib/rt.jar]
    [Loaded java.lang.reflect.InvocationTargetException from /usr/lib/jvm/java-6-openjdk/jre/lib/rt.jar]
    Exception in thread "main" java.lang.reflect.InvocationTargetException
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:616)
        at HelloScalaDump.main(HelloScalaDump.java:41)
    Caused by: java.lang.NoClassDefFoundError: HelloScala$
        at HelloScala.main(Unknown Source)
        ... 5 more
    Caused by: java.lang.ClassNotFoundException: HelloScala$
        at java.lang.ClassLoader.findClass(ClassLoader.java:373)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:321)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:266)
        ... 6 more
    

    似乎HelloScala$正在加载,为什么不能找到它?

    谢谢!

2 个答案:

答案 0 :(得分:1)

Scala使用了很多技巧将它的语义映射到JVM。因此,您会在字节码级别看到很多意想不到的事情。我认为你必须接受它并深入研究scala编译器如何使用Java Structures。

一个类由它的名称和加载类的类加载器(调用define方法的类)标识。你确定加载HelloScala的加载器实际上也加载了HelloScala $吗?

答案 1 :(得分:0)

这对我有用,在每个类''dump'文件中调用dump()方法来加载具有上下文的ClassLoader而不是自定义类加载器的类(因此欺骗类可以访问与类相同的类路径)项目的其余部分):

import java.lang.reflect.*;

public class DumpLoader {

  public static void main(String[] args) throws Exception {
     Class<?> c$ = loadClass("HelloScala$", HelloScala$Dump.dump()); //First load the "anonymous" class
     Class<?> c = loadClass("HelloScala", HelloScalaDump.dump());   //Then load the "real" class
     try {
       Method mainMethod = c.getMethod("main", String[].class);  //Get the main method of the "real" class
       mainMethod.invoke(null, (Object) new String[]{});        //and invoke it to run the spoofed program
     } catch (Exception e) {
      e.printStackTrace();
      System.exit(1);
     }
  }

  private static Class loadClass(String className, byte[] b) {
    //override classDefine (as it is protected) and define the class.
    Class<?> clazz = null;
    try {
     // ClassLoader loader = ClassLoader.getSystemClassLoader();
      ClassLoader loader = Thread.currentThread().getContextClassLoader();
      Class<?> cls = Class.forName("java.lang.ClassLoader");
      Method method = cls.getDeclaredMethod("defineClass", new Class[] { String.class, byte[].class, int.class, int.class });

      // protected method invocaton
      method.setAccessible(true);
      try {
        Object[] args = new Object[] { className, b, new Integer(0), new Integer(b.length)};
        clazz = (Class) method.invoke(loader, args);
      } finally {
        method.setAccessible(false);
      }
    } catch (Exception e) {
      e.printStackTrace();
      System.exit(1);
    }
    return clazz;
  }

}