REVERSE-COMPETITION-VNCTF-2024

news2024/11/16 1:38:16

REVERSE-COMPETITION-VNCTF-2024

    • 前言
    • TBXO
    • baby_c2
    • yun
    • obfuse
    • ko

前言

ko的随机数算法没看出来,可惜~
这里给自己打个广告:东南网安研二在读,求实习,求内推,求老板们多看看我QAQ
blog

TBXO

通过字符串定位到main函数汇编视图
存在混淆,最后retn的返回地址不太会算
调试发现就是几个块在栈上埋下返回地址,通过retn跳转,最后的retn还是会回到main函数
TBXO-main
继续向下调试发现是一个魔改的TEA,多异或了0x33
TBXO-tea
密文和密钥已知,直接解密即可

#include <stdio.h>
#include <stdint.h>

//加密函数
void encrypt(unsigned int num_rounds, uint32_t *v, uint32_t *k)
{
    uint32_t v0 = v[0], v1 = v[1], sum = 0, i;
    uint32_t delta = 0x61C88647;
    uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];
    for (i = 0; i < num_rounds; i++)
    {
        sum -= delta;
        v0 += ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1) ^ 0x33;
        v1 += ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3) ^ 0x33;
    }
    v[0] = v0;
    v[1] = v1;
    printf("sum==0x%x\n", sum);
}

//解密函数
void decrypt(unsigned int num_rounds, uint32_t *v, uint32_t *k)
{
    uint32_t v0 = v[0], v1 = v[1], i;
    uint32_t delta = 0x61C88647, sum = 0xc6ef3720;
    uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];
    for (i = 0; i < num_rounds; i++)
    {
        v1 -= ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3) ^ 0x33;
        v0 -= ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1) ^ 0x33;
        sum += delta;
    }
    v[0] = v0;
    v[1] = v1;
    printf("sum==0x%x\n", sum);
}

//打印数据 hex_or_chr: 1-hex 0-chr
void dump_data(uint32_t *v, int n, bool hex_or_chr)
{
    if (hex_or_chr)
    {
        for (int i = 0; i < n; i++)
        {
            printf("0x%x,", v[i]);
        }
    }
    else
    {
        for (int i = 0; i < n; i++)
        {
            for (int j = 0; j < sizeof(uint32_t) / sizeof(uint8_t); j++)
            {
                printf("%c", (v[i] >> (j * 8)) & 0xFF);
            }
        }
    }
    printf("\n");
    return;
}

int main()
{
    // v为要加解密的数据
    uint32_t v[] = {0x31363010, 0xad938623, 0x8492d4c5, 0x7567e366, 0xc786696b, 0xa0092e31, 0xdb695733, 0xdd13a893, 0x88d8a53e, 0x7e845437};
    // k为加解密密钥,4个32位无符号整数,密钥长度为128位
    uint32_t k[4] = {0x67626463, 0x696D616E, 0x79645F65, 0x6B696C69};
    // num_rounds,建议取值为32
    unsigned int r = 32;

    int n = sizeof(v) / sizeof(uint32_t);
    /*
    printf("加密前明文数据:");
    dump_data(v, n, 1);

    for (int i = 0; i < n / 2; i++)
    {
        encrypt(r, &v[i * 2], k);
    }
    */
    printf("加密后密文数据:");
    dump_data(v, n, 1);

    for (int i = 0; i < n / 2; i++)
    {
        decrypt(r, &v[i * 2], k);
    }

    printf("解密后明文数据:");
    dump_data(v, n, 1);

    printf("解密后明文字符:");
    dump_data(v, n, 0);

    return 0;
}

// VNCTF{Box_obfuscation_and_you_ar_socool}

baby_c2

附件为一段powershell和一个流量包
powershell先解码base64再通过iex运行
将iex替换为Write-Output并运行powershell
baby_c2-base64
发现又套了一层powershell,同样是先解码base64再通过iex运行
通过替换iex为Write-Output打印出解码结果
baby_c2-process
先解码base64,然后启动相应的进程
通过cyberchef解码并保存为exe文件
baby_c2-exe
ida打开该exe文件,调整跳转条件并去除花指令
baby_c2-decode
发现两段SMC,都是异或v3,v3是一个时间相关的值,不知道具体是什么
baby_c2-smc
继续分析发现这两个数组都被解释为了函数指针
一般情况下,函数开头的"push ebp"指令(机器码为0x55)是比较固定的,于是可知v3=0x55^0x8C=0xD9
手动还原代码,分析可知是标准的RC4加密算法
baby_c2-rc4
baby_c2-rc4
查找交叉引用来到,分析可知RC4密钥为文件名
baby_c2-main
在流量包中找到对应的TCP流
baby_c2-tcp
最后直接解密即可
baby_c2-flag

yun

安卓逆向,jadx-gui打开
java层就是简单的长度和格式校验
yun-java
ida打开libyun.so,搜索"java",只有一个静态注册的方法,但很明显方法名不对
yun-so
分析JNI_OnLoad,发现通过RegisterNatives动态注册了flag0_o方法
yun-so
yun-so
yun-so
method_name和method_signature是在init段动态修改的
yun-so
同样通过arr_xor动态修改的变量还有Vnvn,而校验函数会用到这个变量
yun-so
分析method_funcptr,inp传入enc,inp_enc与密文比较
yun-so
分析enc,记录inp和Vnvn的索引,然后对两组索引依次进行2*2的矩阵乘法
yun-so
最后又是一个查表和一个base64编码
yun-so
需要注意的是,密文在JNI_OnLoad也通过arr_xor被动态修改
yun-so
于是最终的解密脚本即为

import base64
import claripy

tab = "abcdefghijklmnopqrstuvwxyz1234567890-"

cipher = [0x62, 0x6a, 0x4a, 0x79, 0x64, 0x58, 0x59, 0x34, 0x4c, 0x58, 0x42, 0x36, 0x63, 0x6e, 0x49, 0x79, 0x5a, 0x6a, 0x4a, 0x78, 0x61, 0x6d, 0x30,
          0x79, 0x64, 0x47, 0x4e, 0x76, 0x4d, 0x33, 0x52, 0x71, 0x4f, 0x47, 0x51, 0x30, 0x65, 0x54, 0x42, 0x70, 0x64, 0x48, 0x70, 0x6f, 0x4d, 0x58, 0x55, 0x74]
cipher = base64.b64decode(bytes(cipher)).decode()
cipher_idx = []
for c in cipher:
    cipher_idx.append(tab.find(c))

key = "s0ry"
key_idx = []
for c in key:
    key_idx.append(tab.find(c))

inp_idx = []
for idx in range(0, len(cipher_idx), 2):
    inp = [claripy.BVS(f"inp_{i}", 16) for i in range(2)]
    s = claripy.Solver()
    s.add((inp[0]*key_idx[0]+inp[1]*key_idx[2]) % len(tab) == cipher_idx[idx])
    s.add((inp[0]*key_idx[1]+inp[1]*key_idx[3]) %
          len(tab) == cipher_idx[idx+1])
    s.add(inp[0] >= 0)
    s.add(inp[0] < len(tab))
    s.add(inp[1] >= 0)
    s.add(inp[1] < len(tab))
    if s.check_satisfiability() == "SAT":
        for i in s.batch_eval(inp, 1):
            for ii in i:
                inp_idx.append(ii)

flag = ""
for idx in inp_idx:
    flag += tab[idx]
print(flag)

# 75531c14-8825-44ed-a9ec-74d47d5cb76b

obfuse

简单去一下jb/jnb这种模式的花指令
obfuse-main

from idaapi import *
from idautils import *
start = 0x0000000000401A10
finish = 0x0000000000407F41
while start <= finish:
    ins1 = get_bytes(start, 6)
    ins2 = get_bytes(start+6, 6)
    if ins1.hex().startswith("0f") and ins2.hex().startswith("0f"):
        addr1 = start+6+ins1[2]
        addr2 = start+6+6+ins2[2]
        if addr1 == addr2:
            patch_bytes(start, b"\x90"*(addr1-start))
        start += 12
    else:
        start += 1

边调试边猜,大致确定如下的校验逻辑
输入经过AES加密后与已知的密文比较,密钥已知,需要找到AES的魔改点
obfuse-main
构造输入为"abcdefghijklmnopqrstuvwxyz123456"
经过不断调试,发现在aes_encrypt函数中存在两步运算xor和aes
obfuse-aes
在进入aes前,输入的前16字节与一段固定的数据异或,也就是CBC模式的初始向量iv
通过异或前后的数据得到这段iv为

0x16, 0x16, 0x1a, 0x1a, 0x1e, 0x1e, 0x1a, 0x1a, 0x16, 0x16, 0x2a, 0x2a, 0x2e, 0x2e, 0x2a, 0x2a

随后进入aes,aes大概魔改了三个地方
obfuse-aes
第一个魔改点在aes_add_round_key,通过调试aes源码,比较数据可知魔改相当于如下效果
obfuse-aes
即在每轮标准的aes_add_round_key后又异或一段固定的数据,该段固定数据可计算得到为

uint8_t xor_arr[] = {0x0, 0x1, 0x2, 0x3, 0x1, 0x0, 0x3, 0x2, 0x2, 0x3, 0x0, 0x1, 0x3, 0x2, 0x1, 0x0};

第二个魔改点在aes_sub_bytes,最后多异或了0xC8
obfuse-aes
第三个魔改点在aes_mix_columns,最后多异或了0x64
obfuse-aes
对应的解密过程即应调整为
obfuse-aes
obfuse-aes
obfuse-aes
解密密文前16字节得到flag前16个字符为"VNCTF{th1s_t@ste"
obfuse-aes
然后调整输入为"VNCTF{th1s_t@steqrstuvwxyz123456"
计算得到输入的后16字节在进入aes前需要异或的初始向量iv为

0xa3,0x81,0x39,0x1a,0xac,0xce,0x11,0xfe,0x12,0xc8,0x7b,0xa9,0x5e,0xc8,0xd,0x5b

于是可解出flag后16个字符为"s_go0d_r1ght~~?}"
obfuse-aes
最后验证成功
obfuse-aes

ko

一堆数据需要异或
ko-xor
这里笔者是先找好数据范围,把数据全都dump出来,把异或运算复制出来,计算完再把结果写回去
ko-strings
通过字符串提示找到标准的rc4_init
ko-rc4
魔改的rc4_encode
ko-rc4
魔改点在于异或运算时对sbox的索引增加了一个随机数
ko-rc4
这个随机数由标准的MT19937算法生成,随机数种子为0xFA11010D
ko-rc4
同样地,通过字符串提示找到密文
ko-rc4
rc4密钥应该在运行过程中被直接打印出来
ko-rc4
于是可先计算得到42个随机数

def _int32(x):
    return int(0xFFFFFFFF & x)


class MT19937:
    def __init__(self, seed):
        self.mt = [0] * 624
        self.mt[0] = seed
        self.mti = 0
        for i in range(1, 624):
            self.mt[i] = _int32(
                1812433253 * (self.mt[i - 1] ^ self.mt[i - 1] >> 30) + i)

    def extract_number(self):
        if self.mti == 0:
            self.twist()
        y = self.mt[self.mti]
        y = y ^ y >> 11
        y = y ^ y << 7 & 2636928640
        y = y ^ y << 15 & 4022730752
        y = y ^ y >> 18
        self.mti = (self.mti + 1) % 624
        return _int32(y)

    def twist(self):
        for i in range(0, 624):
            y = _int32((self.mt[i] & 0x80000000) +
                       (self.mt[(i + 1) % 624] & 0x7fffffff))
            self.mt[i] = (y >> 1) ^ self.mt[(i + 397) % 624]

            if y % 2 != 0:
                self.mt[i] = self.mt[i] ^ 0x9908b0df


mt = MT19937(0xFA11010D)
rand_num = []
for i in range(42):
    rand_num.append(mt.extract_number())
print(rand_num)
# [4194389780, 1470670990, 3026560136, 833079161, 4131466323, 759157580, 4009079223, 2614551579, 513472238, 2993535632, 3748155928, 1410131130, 299129314, 1493771472, 794099837, 1833596110, 2003735283, 2301239837, 2210620102, 1212227044, 286209931, 3671727524, 2298672460, 4293907163, 1382287885, 2958570228, 2560383921, 2598710070, 1618082056, 461162807, 2533884627, 631807209, 1331614827, 3197521366, 586344870, 3568429591, 2977811948, 661755625, 1170402972, 1364250142, 3774156842, 380335765]

然后再进行rc4解密,即可得到flag

#include <stdio.h>

/*
RC4初始化函数
*/
void rc4_init(unsigned char *s, unsigned char *key, unsigned long Len_k)
{
    unsigned int i = 0, j = 0;
    char k[256] = {0};
    unsigned char tmp = 0;
    for (i = 0; i < 256; i++)
    {
        s[i] = i;
        k[i] = key[i % Len_k];
    }
    for (i = 0; i < 256; i++)
    {
        j = (j + s[i] + k[i]) % 256;
        tmp = s[i];
        s[i] = s[j];
        s[j] = tmp;
    }
}

long long int rand_num[42] = {4194389780, 1470670990, 3026560136, 833079161, 4131466323, 759157580, 4009079223, 2614551579, 513472238, 2993535632, 3748155928, 1410131130, 299129314, 1493771472, 794099837, 1833596110, 2003735283, 2301239837, 2210620102, 1212227044, 286209931, 3671727524, 2298672460, 4293907163, 1382287885, 2958570228, 2560383921, 2598710070, 1618082056, 461162807, 2533884627, 631807209, 1331614827, 3197521366, 586344870, 3568429591, 2977811948, 661755625, 1170402972, 1364250142, 3774156842, 380335765};

/*
RC4加解密函数
unsigned char* Data     加解密的数据
unsigned long Len_D     加解密数据的长度
unsigned char* key      密钥
unsigned long Len_k     密钥长度
*/
void rc4_crypt(unsigned char *Data, unsigned long Len_D, unsigned char *key, unsigned long Len_k) //加解密
{
    unsigned char s[256];
    rc4_init(s, key, Len_k);
    int i = 0, j = 0, t = 0;
    unsigned long k = 0;
    unsigned char tmp;
    for (k = 0; k < Len_D; k++)
    {
        i = (i + 1) % 256;
        j = (j + s[i]) % 256;
        tmp = s[i];
        s[i] = s[j];
        s[j] = tmp;
        t = (s[i] + s[j] + rand_num[k]) % 256;
        Data[k] = Data[k] ^ s[t];
    }
}

int main()
{
    //字符串密钥
    /*
    unsigned char key[] = "";
    unsigned long key_len = sizeof(key) - 1;
    */
    //数组密钥
    unsigned char key[] = {98, 51, 66, 108, 98, 110, 78, 122, 97, 67, 49, 114, 90, 88, 107, 116, 100, 106, 69, 65, 65, 65, 65, 65, 66, 71, 53, 118, 98, 109, 85, 65, 65, 65, 65, 69, 98, 109, 57, 117, 90, 81, 65, 65, 65, 65, 65, 65, 65, 65, 65, 66, 65, 65, 65, 66, 108, 119, 65, 65, 65, 65, 100, 122, 99, 50, 103, 116, 99, 110, 78, 104, 65, 65, 65, 65, 65, 119, 69, 65, 65, 81, 65, 65, 65, 89, 69, 65, 119, 108, 49, 120, 68, 99, 65, 104, 98, 100, 109, 52, 57, 122, 57, 77, 69, 74, 117, 86, 107, 121, 119, 50, 106, 70, 106, 65, 65, 73, 86, 89, 116, 89, 73, 90, 50, 68, 51, 98, 75, 48, 89, 112, 75, 76, 117, 79, 89, 55, 117, 113, 116, 70, 79, 115, 106, 67, 79, 49, 73, 52, 55, 90, 76, 99, 50, 110, 71, 87, 102, 114, 118, 97, 120, 116, 67, 111, 50, 56, 78, 53, 54, 73, 101, 53, 66, 108, 118, 68, 104, 106, 107, 113, 108, 113, 78, 90, 106, 76, 119, 104, 65, 86, 67, 78, 76, 87, 110, 116, 67, 115, 88, 74, 110, 56, 120, 116, 51, 118, 104, 67, 69, 102, 57, 99, 99, 69, 105, 78, 75, 103, 74, 81, 122, 73, 118, 88, 115, 102, 57, 54, 85, 104, 50, 107, 55, 54, 99, 88, 76, 105, 54, 121, 113, 122, 118, 106, 122, 109, 78, 110, 107, 117, 113, 105, 80, 118};
    unsigned long key_len = sizeof(key);

    //加解密数据
    unsigned char data[] = {31, 209, 241, 145, 62, 90, 173, 108, 4, 37, 225, 196, 154, 90, 160, 119, 29, 107, 231, 23, 171, 16, 231, 172, 41, 73, 19, 251, 196, 135, 213, 190, 96, 215, 215, 150, 146, 236, 225, 97, 138, 176};
    //加解密
    rc4_crypt(data, sizeof(data), key, key_len);

    for (int i = 0; i < sizeof(data); i++)
    {
        printf("%c", data[i]);
    }

    printf("\n");
    return 0;
}
// VNCTF{Welcome_To_Kernel_World@RETraveler}

感谢大家读到这里
最后再表达一下笔者的求职意愿,老板们可通过站内私信或添加微信P1umH0联系笔者
在此先谢过各位老板

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

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

相关文章

K8S之使用Deployment实现滚动更新

滚动更新 滚动更新简介使用Deployment实现滚动更新相关字段介绍测试滚动更新观察滚动更新查看历史版本 自定义滚动更新策略自定义配置建议实践自定义策略通过 RollingUpdateStrategy 字段来设置滚动更新策略使用Recreate更新策略 滚动更新简介 滚动更新是一种自动化程度较高的…

C++入门06 数据的共享与保护

图源&#xff1a;文心一言 听课笔记简单整理&#xff0c;供小伙伴们参考&#xff0c;内容包含“&#x1f40b;5.2 变量的生存期与可见性、&#x1f40b;5.5 静态成员与静态函数、&#x1f40b;5.6 友元函数与友元类、&#x1f40b;5.7 共享数据的保护 / const关键字、&#x1…

使用Fragments(片段)提升你的Vue.js开发体验

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

JS进阶——深入对象

版权声明 本文章来源于B站上的某马课程&#xff0c;由本人整理&#xff0c;仅供学习交流使用。如涉及侵权问题&#xff0c;请立即与本人联系&#xff0c;本人将积极配合删除相关内容。感谢理解和支持&#xff0c;本人致力于维护原创作品的权益&#xff0c;共同营造一个尊重知识…

docker安装单机版canal和使用

说明&#xff1a;我安装的组件架构如下&#xff1a; 1、准备一台虚拟机&#xff0c;192.168.2.223&#xff0c;我安装的时候&#xff0c;docker只支持canal1.1.6版本&#xff0c;1.1.7无法使用docker安装.还有一点要补充&#xff0c;就是1.1.6好像不支持es8.0以上版本&#x…

基于springboot实现的牙科诊所系统

一、系统架构 前端&#xff1a;html | layui | js | css 后端&#xff1a;springboot | mybatis 环境&#xff1a;jdk1.8 | mysql | maven 二、 代码及数据库 三、功能介绍 01. web端-首页 02. web端-医生介绍 03. web端-新闻资讯 04. web端-关于我们 05. web…

mini-spring|关于Bean对象作用域以及FactoryBean的实现和使用

需求 FactoryBean 直接配置FactoryBean 获取FactoryBean中的Bean对象 FactoryBean的getObject方法通过反射获取Bean对象 由此省去对实体Dao类的定义 解决方法 对外提供一个可以二次从 FactoryBean 的 getObject 方法中获取对象的功能即可 整体架构 整个的实现过程包括了两部…

Spark Bloom Filter Join

1 综述 1.1 目的 Bloom Filter Join&#xff0c;或者说Row-level Runtime Filtering&#xff08;还额外有一条Semi-Join分支&#xff09;&#xff0c;是Spark 3.3对运行时过滤的一个最新补充   之前运行时过滤主要有两个&#xff1a;动态分区裁剪DPP&#xff08;开源实现&am…

electron-release-server部署electron自动更新服务器记录

目录 一、前言 环境 二、步骤 1、下载上传electron-release-server到服务器 2、宝塔新建node项目网站 3、安装依赖 ①npm install ②安装并配置postgres数据库 ③修改项目配置文件 ④启动项目 ⑤修改postgres的认证方式 ⑥Cannot find where you keep your Bower p…

Unity(第十四部)光照

原始的有默认灯光、除了默认的你还可以创建 1、定向光源&#xff08;类似太阳、从无限远的地方射向地面的光&#xff0c;光源位置并不影响照射角度等&#xff0c;不同方向的旋转影响角度和明亮&#xff09; 1. 颜色&#xff1a;调整光的颜色2. 模式&#xff1a;混合是实时加烘…

【InternLM 实战营笔记】LMDeploy 的量化和部署

环境配置 vgpu-smi 查看显卡资源使用情况 新开一个终端执行下面的命令实时观察 GPU 资源的使用情况。 watch vgpu-smi复制环境到我们自己的 conda 环境 /root/share/install_conda_env_internlm_base.sh lmdeploy激活环境 conda activate lmdeploy安装依赖库 # 解决 Modu…

SpringBoot项目中如何结合Mybatis进行数据库查询

在Spring Boot项目中使用Mybatis进行数据库操作是一种常见的实现方式。下面我将展示如何在Spring Boot项目中整合Mybatis。这个示例将包括几个主要部分&#xff1a;项目依赖配置、配置文件、实体类、Mapper接口及其XML配置文件、服务类、以及一个简单的控制器。 1. 项目依赖配…

MyBatis-Plus 快速入门

介绍 j​​​​​MyBatis-Plus (opens new window)&#xff08;简称 MP&#xff09;是一个 MyBatis (opens new window)的增强工具&#xff0c;在 MyBatis 的基础上只做增强不做改变&#xff0c;为简化开发、提高效率而生。 官网&#xff1a;MyBatis-Plus (baomidou.com) 1.…

sheng的学习笔记-卷积神经网络经典架构-LeNet-5、AlexNet、VGGNet-16

目录&#xff1a;目录 看本文章之前&#xff0c;需要学习卷积神经网络基础&#xff0c;可参考 sheng的学习笔记-卷积神经网络-CSDN博客 目录 LeNet-5 架构图 层级解析 1、输入层&#xff08;Input layer&#xff09; 2、卷积层C1&#xff08;Convolutional layer C1&…

2W字-35页PDF谈谈自己对QT某些知识点的理解

2W字-35页PDF谈谈自己对QT某些知识点的理解 前言与总结总体知识点的概况一些笔记的概况笔记阅读清单 前言与总结 最近&#xff0c;也在对自己以前做的项目做一个知识点的梳理&#xff0c;发现可能自己以前更多的是用某个控件&#xff0c;以及看官方手册&#xff0c;但是没有更…

卷积神经网络(CNN)原理与实现

卷积神经网络(CNN) 卷积神经网络原理卷积神经网络的数学推导卷积层反向传播算法数学推导卷积层实现代码 卷积神经网络(CNN) 卷积神经网络原理 卷积神经网络是一种用于图像、语音、自然语言等数据的深度学习模型&#xff0c;其核心思想是使用卷积操作提取输入数据的特征&…

tmux 工具常用命令

Tmux 是一个终端复用器&#xff08;terminal multiplexer&#xff09;&#xff0c;类似于 GNU screen 非常有用&#xff0c;属于常用的运维管理工具。 安装步骤 Ubuntu apt install tmux centos yum install tmux常用命令 以下所有快捷键&#xff0c;均是 ctrlb 按完之后先…

SpringBoot+Vue全栈开发-刘老师教编程(b站)(二)

创建SpringBoot项目 1.配置maven 出现bug java: 无法访问org.springframework.boot.SpringApplication 错误的类文件: /D:/maven/repository/org/springframework/boot/spring-boot/3.0.0/spring-boot-3.0.0.jar!/org/springframework/boot/SpringApplication.class 类…

react-router 源码之matchPath方法

1. 基础依赖path-to-regexp react-router提供了专门的路由匹配方法matchPath(位于packages/react-router/modules/matchPath.js)&#xff0c;该方法背后依赖的其实是path-to-regexp包。 path-to-regexp输入是路径字符串&#xff08;也就是Route中定义的path的值&#xff09;&…

AI:145-智能监控系统下的行人安全预警与法律合规分析

🚀点击这里跳转到本专栏,可查阅专栏顶置最新的指南宝典~ 🎉🎊🎉 你的技术旅程将在这里启航! 从基础到实践,深入学习。无论你是初学者还是经验丰富的老手,对于本专栏案例和项目实践都有参考学习意义。 ✨✨✨ 每一个案例都附带关键代码,详细讲解供大家学习,希望…