爆赞好文之java动态字节码加载(TemplatesImpl)简谈

动态字节码加载

虽然已经学过了,不过忘完了,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

  • 23
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值